/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * fn-math.c: Built in mathematical functions and functions registration * * Authors: * Miguel de Icaza (miguel@gnu.org) * Jukka-Pekka Iivonen (iivonen@iki.fi) * Morten Welinder (terra@diku.dk) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plugin.h" #include "plugin-util.h" #include "module-plugin-defs.h" GNUMERIC_MODULE_PLUGIN_INFO_DECL; typedef struct { GSList *list; int num; } math_sums_t; static GnmValue * callback_function_sumxy (Sheet *sheet, int col, int row, GnmCell *cell, void *user_data) { math_sums_t *mm = user_data; gnm_float x; gpointer p; if (cell == NULL) return NULL; cell_eval (cell); switch (cell->value->type) { case VALUE_ERROR: return VALUE_TERMINATE; case VALUE_BOOLEAN: x = cell->value->v_bool.val ? 1 : 0; break; case VALUE_INTEGER: x = cell->value->v_int.val; break; case VALUE_FLOAT: x = cell->value->v_float.val; break; case VALUE_EMPTY: default: return NULL; } p = g_new (gnm_float, 1); *((gnm_float *) p) = x; mm->list = g_slist_append (mm->list, p); mm->num++; return NULL; } typedef struct { GSList *list; criteria_test_fun_t fun; GnmValue *test_value; int num; int total_num; gboolean actual_range; gnm_float sum; GSList *current; } math_criteria_t; static GnmValue * callback_function_criteria (Sheet *sheet, int col, int row, GnmCell *cell, void *user_data) { math_criteria_t *mm = user_data; GnmValue *v; mm->total_num++; if (cell != NULL) { cell_eval (cell); switch (cell->value->type) { case VALUE_BOOLEAN: case VALUE_INTEGER: case VALUE_FLOAT: case VALUE_STRING: v = value_dup (cell->value); break; case VALUE_EMPTY: default: v = NULL; } } else v = NULL; if (mm->fun (v, mm->test_value)) { if (mm->actual_range) { mm->list = g_slist_append (mm->list, GINT_TO_POINTER (mm->total_num)); if (v != NULL) value_release (v); } else mm->list = g_slist_append (mm->list, v); mm->num++; } else if (v != NULL) value_release (v); return NULL; } /***************************************************************************/ static char const *help_gcd = { N_("@FUNCTION=GCD\n" "@SYNTAX=GCD(number1,number2,...)\n" "@DESCRIPTION=" "GCD returns the greatest common divisor of given numbers.\n" "\n" "* If any of the arguments is less than one, GCD returns #NUM! " "error.\n" "* If any of the arguments is non-integer, it is truncated.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "GCD(470,770) equals 10.\n" "GCD(470,770,1495) equals 5.\n" "\n" "@SEEALSO=LCM") }; static int range_gcd (gnm_float const *xs, int n, gnm_float *res) { if (n > 0) { int i; int gcd_so_far = 0; for (i = 0; i < n; i++) { gnm_float thisx = gnumeric_fake_floor (xs[i]); if (thisx <= 0 || thisx > INT_MAX) return 1; else gcd_so_far = gcd ((int)thisx, gcd_so_far); } *res = gcd_so_far; return 0; } else return 1; } static GnmValue * gnumeric_gcd (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_gcd, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_NUM); } /***************************************************************************/ static char const *help_lcm = { N_("@FUNCTION=LCM\n" "@SYNTAX=LCM(number1,number2,...)\n" "@DESCRIPTION=" "LCM returns the least common multiple of integers. The least " "common multiple is the smallest positive number that is a " "multiple of all integer arguments given.\n" "\n" "* If any of the arguments is less than one, LCM returns #NUM!.\n" "* If any of the arguments is non-integer, it is truncated.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "LCM(2,13) equals to 26.\n" "LCM(4,7,5) equals to 140.\n" "\n" "@SEEALSO=GCD") }; static int range_lcm (gnm_float const *xs, int n, gnm_float *res) { if (n > 0) { int i; int lcm_so_far = 1; for (i = 0; i < n; i++) { gnm_float x = gnumeric_fake_floor (xs[i]); if (x <= 0 || x > INT_MAX) return 1; else { int xi = (int) x; lcm_so_far /= gcd (lcm_so_far, xi); lcm_so_far *= xi; } } *res = lcm_so_far; return 0; } else return 1; } static GnmValue * gnumeric_lcm (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_lcm, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_NUM); } /***************************************************************************/ static char const *help_hypot = { N_("@FUNCTION=HYPOT\n" "@SYNTAX=HYPOT(number1,number2,...)\n" "@DESCRIPTION=" "HYPOT returns the square root of the sum of the squares of the argumnents.\n" "\n" "@EXAMPLES=\n" "HYPOT(3,4) equals to 5.\n" "\n" "@SEEALSO=MIN,MAX") }; static GnmValue * gnumeric_hypot (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_hypot, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_NUM); } /***************************************************************************/ static char const *help_abs = { N_("@FUNCTION=ABS\n" "@SYNTAX=ABS(b1)\n" "@DESCRIPTION=" "ABS implements the Absolute Value function: the result is " "to drop the negative sign (if present). This can be done for " "integers and floating point numbers.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ABS(7) equals 7.\n" "ABS(-3.14) equals 3.14.\n" "\n" "@SEEALSO=CEIL, CEILING, FLOOR, INT, MOD") }; static GnmValue * gnumeric_abs (FunctionEvalInfo *ei, GnmValue **args) { return value_new_float (gnumabs (value_get_as_float (args [0]))); } /***************************************************************************/ static char const *help_acos = { N_("@FUNCTION=ACOS\n" "@SYNTAX=ACOS(x)\n" "@DESCRIPTION=" "ACOS function calculates the arc cosine of @x; that " "is the value whose cosine is @x.\n" "\n" "* The value it returns is in radians.\n" "* If @x falls outside the range -1 to 1, ACOS returns " "the #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ACOS(0.1) equals 1.470629.\n" "ACOS(-0.1) equals 1.670964.\n" "\n" "@SEEALSO=COS, SIN, DEGREES, RADIANS") }; static GnmValue * gnumeric_acos (FunctionEvalInfo *ei, GnmValue **args) { gnm_float t; t = value_get_as_float (args [0]); if ((t < -1.0) || (t > 1.0)) return value_new_error_NUM (ei->pos); return value_new_float (acosgnum (t)); } /***************************************************************************/ static char const *help_acosh = { N_("@FUNCTION=ACOSH\n" "@SYNTAX=ACOSH(x)\n" "@DESCRIPTION=" "ACOSH function calculates the inverse hyperbolic " "cosine of @x; that is the value whose hyperbolic cosine is " "@x.\n" "\n" "* If @x is less than 1.0, ACOSH() returns the #NUM! error.\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "ACOSH(2) equals 1.31696.\n" "ACOSH(5.3) equals 2.35183.\n" "\n" "@SEEALSO=ACOS, ASINH, DEGREES, RADIANS ") }; static GnmValue * gnumeric_acosh (FunctionEvalInfo *ei, GnmValue **args) { gnm_float t; t = value_get_as_float (args [0]); if (t < 1.0) return value_new_error_NUM (ei->pos); return value_new_float (acoshgnum (t)); } /***************************************************************************/ static char const *help_asin = { N_("@FUNCTION=ASIN\n" "@SYNTAX=ASIN(x)\n" "@DESCRIPTION=" "ASIN function calculates the arc sine of @x; that is " "the value whose sine is @x.\n" "\n" "* If @x falls outside the range -1 to 1, ASIN returns " "the #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ASIN(0.5) equals 0.523599.\n" "ASIN(1) equals 1.570797.\n" "\n" "@SEEALSO=SIN, COS, ASINH, DEGREES, RADIANS") }; static GnmValue * gnumeric_asin (FunctionEvalInfo *ei, GnmValue **args) { gnm_float t; t = value_get_as_float (args [0]); if ((t < -1.0) || (t > 1.0)) return value_new_error_NUM (ei->pos); return value_new_float (asingnum (t)); } /***************************************************************************/ static char const *help_asinh = { N_("@FUNCTION=ASINH\n" "@SYNTAX=ASINH(x)\n" "@DESCRIPTION=" "ASINH function calculates the inverse hyperbolic sine of @x; " "that is the value whose hyperbolic sine is @x.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ASINH(0.5) equals 0.481212.\n" "ASINH(1.0) equals 0.881374.\n" "\n" "@SEEALSO=ASIN, ACOSH, SIN, COS, DEGREES, RADIANS") }; static GnmValue * gnumeric_asinh (FunctionEvalInfo *ei, GnmValue **args) { return value_new_float (asinhgnum (value_get_as_float (args [0]))); } /***************************************************************************/ static char const *help_atan = { N_("@FUNCTION=ATAN\n" "@SYNTAX=ATAN(x)\n" "@DESCRIPTION=" "ATAN function calculates the arc tangent of @x; that " "is the value whose tangent is @x.\n\n" "* Return value is in radians.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ATAN(0.5) equals 0,463648.\n" "ATAN(1) equals 0,785398.\n" "\n" "@SEEALSO=TAN, COS, SIN, DEGREES, RADIANS") }; static GnmValue * gnumeric_atan (FunctionEvalInfo *ei, GnmValue **args) { return value_new_float (atangnum (value_get_as_float (args [0]))); } /***************************************************************************/ static char const *help_atanh = { N_("@FUNCTION=ATANH\n" "@SYNTAX=ATANH(x)\n" "@DESCRIPTION=" "ATANH function calculates the inverse hyperbolic tangent " "of @x; that is the value whose hyperbolic tangent is @x.\n" "\n" "* If the absolute value of @x is greater than 1.0, ATANH " "returns #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ATANH(0.5) equals 0.549306.\n" "ATANH(0.8) equals 1.098612.\n" "\n" "@SEEALSO=ATAN, TAN, SIN, COS, DEGREES, RADIANS") }; static GnmValue * gnumeric_atanh (FunctionEvalInfo *ei, GnmValue **args) { gnm_float t; t = value_get_as_float (args [0]); if ((t <= -1.0) || (t >= 1.0)) return value_new_error_NUM (ei->pos); return value_new_float (atanhgnum (value_get_as_float (args [0]))); } /***************************************************************************/ static char const *help_atan2 = { N_("@FUNCTION=ATAN2\n" "@SYNTAX=ATAN2(b1,b2)\n" "@DESCRIPTION=" "ATAN2 function calculates the arc tangent of the two " "variables @b1 and @b2. It is similar to calculating the arc " "tangent of @b2 / @b1, except that the signs of both arguments " "are used to determine the quadrant of the result.\n\n" "* The result is in radians.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ATAN2(0.5,1.0) equals 1.107149.\n" "ATAN2(-0.5,2.0) equals 1.815775.\n" "\n" "@SEEALSO=ATAN, ATANH, COS, SIN, DEGREES, RADIANS") }; static GnmValue * gnumeric_atan2 (FunctionEvalInfo *ei, GnmValue **args) { return value_new_float (atan2gnum (value_get_as_float (args [1]), value_get_as_float (args [0]))); } /***************************************************************************/ static char const *help_ceil = { N_("@FUNCTION=CEIL\n" "@SYNTAX=CEIL(x)\n" "@DESCRIPTION=" "CEIL function rounds @x up to the next nearest integer.\n\n" "\n" "@EXAMPLES=\n" "CEIL(0.4) equals 1.\n" "CEIL(-1.1) equals -1.\n" "CEIL(-2.9) equals -2.\n" "\n" "@SEEALSO=CEILING, FLOOR, ABS, INT, MOD") }; static GnmValue * gnumeric_ceil (FunctionEvalInfo *ei, GnmValue **args) { return value_new_float (gnumeric_fake_ceil (value_get_as_float (args [0]))); } /***************************************************************************/ static char const *help_countif = { N_("@FUNCTION=COUNTIF\n" "@SYNTAX=COUNTIF(range,criteria)\n" "@DESCRIPTION=" "COUNTIF function counts the number of cells in the given @range " "that meet the given @criteria.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "23, 27, 28, 33, and 39. Then\n" "COUNTIF(A1:A5,\"<=28\") equals 3.\n" "COUNTIF(A1:A5,\"<28\") equals 2.\n" "COUNTIF(A1:A5,\"28\") equals 1.\n" "COUNTIF(A1:A5,\">28\") equals 2.\n" "\n" "@SEEALSO=COUNT,SUMIF") }; static GnmValue * gnumeric_countif (FunctionEvalInfo *ei, GnmValue **argv) { GnmValue *range = argv[0]; Sheet *sheet; CellIterFlags iter_flags; math_criteria_t items; GnmValue *ret; GSList *list; items.num = 0; items.total_num = 0; items.list = NULL; items.actual_range = FALSE; if ((!VALUE_IS_NUMBER (argv[1]) && argv[1]->type != VALUE_STRING) || (range->type != VALUE_CELLRANGE)) return value_new_error_VALUE (ei->pos); parse_criteria (argv[1], &items.fun, &items.test_value, &iter_flags, workbook_date_conv (ei->pos->sheet->workbook)); sheet = eval_sheet (range->v_range.cell.a.sheet, ei->pos->sheet); ret = sheet_foreach_cell_in_range (sheet, iter_flags, range->v_range.cell.a.col, range->v_range.cell.a.row, range->v_range.cell.b.col, range->v_range.cell.b.row, callback_function_criteria, &items); value_release (items.test_value); if (ret != NULL) return value_new_error_VALUE (ei->pos); list = items.list; while (list != NULL) { if (list->data != NULL) value_release (list->data); list = list->next; } g_slist_free (items.list); return value_new_int (items.num); } /***************************************************************************/ static char const *help_sumif = { N_("@FUNCTION=SUMIF\n" "@SYNTAX=SUMIF(range,criteria[,actual_range])\n" "@DESCRIPTION=" "SUMIF function sums the values in the given @range that meet " "the given @criteria. If @actual_range is given, SUMIF sums " "the values in the @actual_range whose corresponding components " "in @range meet the given @criteria.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "23, 27, 28, 33, and 39. Then\n" "SUMIF(A1:A5,\"<=28\") equals 78.\n" "SUMIF(A1:A5,\"<28\") equals 50.\n" "In addition, if the cells B1, B2, ..., B5 hold numbers " "5, 3, 2, 6, and 7 then:\n" "SUMIF(A1:A5,\"<=27\",B1:B5) equals 8.\n" "\n" "@SEEALSO=COUNTIF, SUM") }; static GnmValue * callback_function_sumif (Sheet *sheet, int col, int row, GnmCell *cell, void *user_data) { math_criteria_t *mm = user_data; gnm_float v = 0.; /* If we have finished the list there is no need to bother */ if (mm->current == NULL) return NULL; /* We have not reached the next selected element yet. * This implies that summing a range containing an error * where the criteria does not select the error is OK. */ if (++(mm->total_num) != GPOINTER_TO_INT (mm->current->data)) return NULL; if (cell != NULL) { cell_eval (cell); switch (cell->value->type) { case VALUE_BOOLEAN: v = cell->value->v_bool.val ? 1 : 0; break; case VALUE_INTEGER: v = cell->value->v_int.val; break; case VALUE_FLOAT: v = cell->value->v_float.val; break; case VALUE_STRING: case VALUE_EMPTY: break; default: return VALUE_TERMINATE; } mm->sum += v; } mm->current = mm->current->next; return NULL; } static GnmValue * gnumeric_sumif (FunctionEvalInfo *ei, GnmValue **argv) { GnmValue *range = argv[0]; GnmValue *actual_range = argv[2]; math_criteria_t items; GnmValue *ret; gnm_float sum; GSList *list; CellIterFlags iter_flags; items.num = 0; items.total_num = 0; items.list = NULL; if (range->type != VALUE_CELLRANGE || !(VALUE_IS_NUMBER (argv[1]) || argv[1]->type == VALUE_STRING)) return value_new_error_VALUE (ei->pos); parse_criteria (argv[1], &items.fun, &items.test_value, &iter_flags, workbook_date_conv (ei->pos->sheet->workbook)); items.actual_range = (actual_range != NULL); ret = sheet_foreach_cell_in_range ( eval_sheet (range->v_range.cell.a.sheet, ei->pos->sheet), /* * Do not ignore empty cells if there is a * target range. We need the orders of the source values to * line up with the values of the target range. */ (actual_range == NULL) ? iter_flags : CELL_ITER_ALL, range->v_range.cell.a.col, range->v_range.cell.a.row, range->v_range.cell.b.col, range->v_range.cell.b.row, callback_function_criteria, &items); value_release (items.test_value); if (ret != NULL) return value_new_error_VALUE (ei->pos); if (actual_range == NULL) { list = items.list; sum = 0; while (list != NULL) { GnmValue *v = list->data; if (v != NULL) { sum += value_get_as_float (v); value_release (v); } list = list->next; } } else { items.current = items.list; items.sum = items.total_num = 0; ret = sheet_foreach_cell_in_range ( eval_sheet (actual_range->v_range.cell.a.sheet, ei->pos->sheet), /* Empty cells too. Criteria and results must align */ CELL_ITER_ALL, actual_range->v_range.cell.a.col, actual_range->v_range.cell.a.row, actual_range->v_range.cell.b.col, actual_range->v_range.cell.b.row, callback_function_sumif, &items); sum = items.sum; } g_slist_free (items.list); return value_new_float (sum); } /***************************************************************************/ static char const *help_ceiling = { N_("@FUNCTION=CEILING\n" "@SYNTAX=CEILING(x,significance)\n" "@DESCRIPTION=" "CEILING function rounds @x up to the nearest multiple of " "@significance.\n" "\n" "* If @x or @significance is non-numeric CEILING returns " "#VALUE! error.\n" "* If @x and @significance have different signs CEILING returns " "#NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "CEILING(2.43,1) equals 3.\n" "CEILING(123.123,3) equals 126.\n" "\n" "@SEEALSO=CEIL, FLOOR, ABS, INT, MOD") }; static GnmValue * gnumeric_ceiling (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number = value_get_as_float (argv[0]); gnm_float s; if (argv[1] == NULL) s = (number >= 0) ? 1.0 : -1.0; else { s = value_get_as_float (argv[1]); } if (s == 0) return value_new_error_DIV0 (ei->pos); if (number / s < 0) return value_new_error_NUM (ei->pos); return value_new_float (gnumeric_fake_ceil (number / s) * s); } /***************************************************************************/ static char const *help_cos = { N_("@FUNCTION=COS\n" "@SYNTAX=COS(x)\n" "@DESCRIPTION=" "COS function returns the cosine of @x, where @x is given " "in radians.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "COS(0.5) equals 0.877583.\n" "COS(1) equals 0.540302.\n" "\n" "@SEEALSO=COSH, SIN, SINH, TAN, TANH, RADIANS, DEGREES") }; static GnmValue * gnumeric_cos (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (cosgnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_cosh = { N_("@FUNCTION=COSH\n" "@SYNTAX=COSH(x)\n" "@DESCRIPTION=" "COSH function returns the hyperbolic cosine of @x, which " "is defined mathematically as\n\n\t(exp(@x) + exp(-@x)) / 2.\n\n" "* @x is in radians.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "COSH(0.5) equals 1.127626.\n" "COSH(1) equals 1.543081.\n" "\n" "@SEEALSO=COS, SIN, SINH, TAN, TANH, RADIANS, DEGREES, EXP") }; static GnmValue * gnumeric_cosh (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (coshgnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_degrees = { N_("@FUNCTION=DEGREES\n" "@SYNTAX=DEGREES(x)\n" "@DESCRIPTION=" "DEGREES computes the number of degrees equivalent to @x radians.\n" "\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "DEGREES(2.5) equals 143.2394.\n" "\n" "@SEEALSO=RADIANS, PI") }; static GnmValue * gnumeric_degrees (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float ((value_get_as_float (argv [0]) * 180.0) / M_PIgnum); } /***************************************************************************/ static char const *help_exp = { N_("@FUNCTION=EXP\n" "@SYNTAX=EXP(x)\n" "@DESCRIPTION=" "EXP computes the value of e (the base of natural logarithms) " "raised to the power of @x.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "EXP(2) equals 7.389056.\n" "\n" "@SEEALSO=LOG, LOG2, LOG10") }; static GnmValue * gnumeric_exp (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (expgnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_expm1 = { N_("@FUNCTION=EXPM1\n" "@SYNTAX=EXPM1(x)\n" "@DESCRIPTION=" "EXPM1 computes EXP(@x)-1 with higher resulting precision than " "the direct formula.\n\n" "@EXAMPLES=\n" "EXPM1(0.01) equals 0.01005.\n" "\n" "@SEEALSO=EXP, LN1P") }; static GnmValue * gnumeric_expm1 (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (expm1gnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_fact = { N_("@FUNCTION=FACT\n" "@SYNTAX=FACT(x)\n" "@DESCRIPTION=" "FACT computes the factorial of @x. ie, @x!\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "FACT(3) equals 6.\n" "FACT(9) equals 362880.\n" "\n" "@SEEALSO=") }; static GnmValue * gnumeric_fact (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float x = value_get_as_float (argv[0]); gboolean x_is_integer = (x == floorgnum (x)); if (x < 0 && x_is_integer) return value_new_error_NUM (ei->pos); if (x > 12 || !x_is_integer) { int sign; gnm_float tmp = lgamma_rgnum (x + 1, &sign); gnm_float res = sign * expgnum (tmp); if (x_is_integer) res = floorgnum (res + 0.5); /* Round, just in case. */ return value_new_float (res); } else return value_new_int (fact (x)); } /***************************************************************************/ static char const *help_beta = { N_("@FUNCTION=BETA\n" "@SYNTAX=BETA(a,b)\n" "@DESCRIPTION=" "BETA function returns the value of the mathematic beta function " "extended to all real numbers except 0 and negative integers.\n" "\n" "* If @a, @b, or (@a + @b) are non-positive integers, BETA returns #NUM! " "error.\n" "\n" "@EXAMPLES=\n" "BETA(2,3) equals 0.083333.\n" "BETA(-0.5,0.5) equals #NUM!.\n" "\n" "@SEEALSO=BETALN,GAMMALN") }; static GnmValue * gnumeric_beta (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float a = value_get_as_float (argv[0]); gnm_float b = value_get_as_float (argv[1]); #warning "Improve error handling. Relying on value_new_float to do it is cheesy" return value_new_float (beta (a, b)); } /***************************************************************************/ static char const *help_betaln = { N_("@FUNCTION=BETALN\n" "@SYNTAX=BETALN(a,b)\n" "@DESCRIPTION=" "BETALN function returns the natural logarithm of the " "absolute value of the beta function.\n" "\n" "* If @a, @b, or (@a + @b) are non-positive integers, BETALN returns #NUM! " "\n" "@EXAMPLES=\n" "BETALN(2,3) equals -2.48.\n" "BETALN(-0.5,0.5) equals #NUM!.\n" "\n" "@SEEALSO=BETA,GAMMALN") }; static GnmValue * gnumeric_betaln (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float a = value_get_as_float (argv[0]); gnm_float b = value_get_as_float (argv[1]); int sign; return value_new_float (lbeta3 (a, b, &sign)); } /***************************************************************************/ static char const *help_combin = { N_("@FUNCTION=COMBIN\n" "@SYNTAX=COMBIN(n,k)\n" "@DESCRIPTION=" "COMBIN computes the number of combinations.\n" "\n" "* Performing this function on a non-integer or a negative number " "returns #NUM! error.\n" "* If @n is less than @k COMBIN returns #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "COMBIN(8,6) equals 28.\n" "COMBIN(6,2) equals 15.\n" "\n" "@SEEALSO=") }; static GnmValue * gnumeric_combin (FunctionEvalInfo *ei, GnmValue **argv) { int n = value_get_as_int (argv[0]); int k = value_get_as_int (argv[1]); if (k >= 0 && n >= k) return value_new_float (combin (n ,k)); return value_new_error_NUM (ei->pos); } /***************************************************************************/ static char const *help_floor = { N_("@FUNCTION=FLOOR\n" "@SYNTAX=FLOOR(x[,significance])\n" "@DESCRIPTION=" "FLOOR function rounds @x down to the next nearest multiple " "of @significance.\n\n" "* @significance defaults to 1.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "FLOOR(0.5) equals 0.\n" "FLOOR(5,2) equals 4.\n" "FLOOR(-5,-2) equals -4.\n" "FLOOR(-5,2) equals #NUM!.\n" "\n" "@SEEALSO=CEIL, CEILING, ABS, INT, MOD") }; static GnmValue * gnumeric_floor (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, s; number = value_get_as_float (argv[0]); if (argv[1] == NULL) s = (number >= 0) ? 1.0 : -1.0; else s = value_get_as_float (argv[1]); if (s == 0) return value_new_error_DIV0 (ei->pos); if (number / s < 0) return value_new_error_NUM (ei->pos); return value_new_float (gnumeric_fake_floor (number / s) * s); } /***************************************************************************/ static char const *help_int = { N_("@FUNCTION=INT\n" "@SYNTAX=INT(a)\n" "@DESCRIPTION=" "INT function returns the largest integer that is not " "bigger than its argument.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "INT(7.2) equals 7.\n" "INT(-5.5) equals -6.\n" "\n" "@SEEALSO=CEIL, CEILING, FLOOR, ABS, MOD") }; static GnmValue * gnumeric_int (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (gnumeric_fake_floor (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_log = { N_("@FUNCTION=LOG\n" "@SYNTAX=LOG(x[,base])\n" "@DESCRIPTION=" "LOG computes the logarithm of @x in the given base @base. " "If no @base is given LOG returns the logarithm in base 10. " "@base must be > 0. and cannot equal 1.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "LOG(2) equals 0.30103.\n" "LOG(8192,2) equals 13.\n" "\n" "@SEEALSO=LN, LOG2, LOG10") }; static GnmValue * gnumeric_log (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float t = value_get_as_float (argv [0]); gnm_float base = argv[1] ? value_get_as_float (argv[1]) : 10; if (base == 1. || base <= 0.) return value_new_error_NUM (ei->pos); if (t <= 0.0) return value_new_error_NUM (ei->pos); return value_new_float (loggnum (t) / loggnum (base)); } /***************************************************************************/ static char const *help_ln = { N_("@FUNCTION=LN\n" "@SYNTAX=LN(x)\n" "@DESCRIPTION=" "LN returns the natural logarithm of @x.\n" "\n" "* If @x <= 0, LN returns #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "LN(7) equals 1.94591.\n" "\n" "@SEEALSO=EXP, LOG2, LOG10") }; static GnmValue * gnumeric_ln (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float t = value_get_as_float (argv [0]); if (t <= 0.0) return value_new_error_NUM (ei->pos); return value_new_float (loggnum (t)); } /***************************************************************************/ static char const *help_ln1p = { N_("@FUNCTION=LN1P\n" "@SYNTAX=LN1P(x)\n" "@DESCRIPTION=" "LN1P computes LN(1+@x) with higher resulting precision than " "the direct formula.\n" "\n" "* If @x <= -1, LN1P returns #NUM! error.\n" "\n" "@EXAMPLES=\n" "LN1P(0.01) equals 0.00995.\n" "\n" "@SEEALSO=LN, EXPM1") }; static GnmValue * gnumeric_ln1p (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float t = value_get_as_float (argv [0]); if (t <= -1) return value_new_error_NUM (ei->pos); return value_new_float (log1pgnum (t)); } /***************************************************************************/ static char const *help_power = { N_("@FUNCTION=POWER\n" "@SYNTAX=POWER(x,y)\n" "@DESCRIPTION=" "POWER returns the value of @x raised to the power @y.\n\n" "\n" "* If both @x and @y equals to 0, POWER returns #NUM! error.\n" "* If @x = 0 and @y < 0, POWER returns #DIV/0! error.\n" "* If @x < 0 and @y is non-integer, POWER returns #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "POWER(2,7) equals 128.\n" "POWER(3,3.141) equals 31.523749.\n" "\n" "@SEEALSO=EXP") }; static GnmValue * gnumeric_power (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float x = value_get_as_float (argv [0]); gnm_float y = value_get_as_float (argv [1]); if ((x > 0) || (x == 0 && y > 0) || (x < 0 && y == floorgnum (y))) return value_new_float (powgnum (x, y)); if (x == 0 && y != 0) return value_new_error_DIV0 (ei->pos); else return value_new_error_NUM (ei->pos); } /***************************************************************************/ static char const *help_log2 = { N_("@FUNCTION=LOG2\n" "@SYNTAX=LOG2(x)\n" "@DESCRIPTION=" "LOG2 computes the base-2 logarithm of @x.\n\n" "* If @x <= 0, LOG2 returns #NUM! error.\n" "\n" "@EXAMPLES=\n" "LOG2(1024) equals 10.\n" "\n" "@SEEALSO=EXP, LOG10, LOG") }; static GnmValue * gnumeric_log2 (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float t = value_get_as_float (argv [0]); if (t <= 0.0) return value_new_error_NUM (ei->pos); return value_new_float (loggnum (t) / M_LN2gnum); } /***************************************************************************/ static char const *help_log10 = { N_("@FUNCTION=LOG10\n" "@SYNTAX=LOG10(x)\n" "@DESCRIPTION=" "LOG10 computes the base-10 logarithm of @x.\n\n" "* If @x <= 0, LOG10 returns #NUM! error.\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "LOG10(7) equals 0.845098.\n" "\n" "@SEEALSO=EXP, LOG2, LOG") }; static GnmValue * gnumeric_log10 (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float t; t = value_get_as_float (argv [0]); if (t <= 0.0) return value_new_error_NUM (ei->pos); return value_new_float (log10gnum (t)); } /***************************************************************************/ static char const *help_mod = { N_("@FUNCTION=MOD\n" "@SYNTAX=MOD(number,divisor)\n" "@DESCRIPTION=" "MOD function returns the remainder when @divisor is divided " "into @number.\n" "\n" "* MOD returns #DIV/0! if @divisor is zero.\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "MOD(23,7) equals 2.\n" "\n" "@SEEALSO=CEIL, CEILING, FLOOR, ABS, INT, ABS") }; /* * MOD(-1,-3) = -1 * MOD(2,-3) = -2 * MOD(10.6,2) = 0.6 * MOD(-10.6,2) = 1.4 * MOD(10.6,-2) = -0.6 * MOD(-10.6,-2) = -1.4 */ static GnmValue * gnumeric_mod (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float a, b, babs, r; a = value_get_as_float (argv[0]); b = value_get_as_float (argv[1]); if (b == 0) return value_new_error_DIV0 (ei->pos); babs = gnumabs (b); r = fmodgnum (gnumabs (a), babs); if (r > 0) { if ((a < 0) != (b < 0)) r = babs - r; if (b < 0) r = -r; } return value_new_float (r); } /***************************************************************************/ static char const *help_radians = { N_("@FUNCTION=RADIANS\n" "@SYNTAX=RADIANS(x)\n" "@DESCRIPTION=" "RADIANS computes the number of radians equivalent to @x degrees.\n" "\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "RADIANS(180) equals 3.14159.\n" "\n" "@SEEALSO=PI,DEGREES") }; static GnmValue * gnumeric_radians (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float ((value_get_as_float (argv [0]) * M_PIgnum) / 180); } /***************************************************************************/ static char const *help_sin = { N_("@FUNCTION=SIN\n" "@SYNTAX=SIN(x)\n" "@DESCRIPTION=" "SIN function returns the sine of @x, where @x is given " "in radians.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "SIN(0.5) equals 0.479426.\n" "\n" "@SEEALSO=COS, COSH, SINH, TAN, TANH, RADIANS, DEGREES") }; static GnmValue * gnumeric_sin (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (singnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_sinh = { N_("@FUNCTION=SINH\n" "@SYNTAX=SINH(x)\n" "@DESCRIPTION=" "SINH function returns the hyperbolic sine of @x, " "which is defined mathematically as\n\n\t" "(exp(@x) - exp(-@x)) / 2.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "SINH(0.5) equals 0.521095.\n" "\n" "@SEEALSO=SIN, COS, COSH, TAN, TANH, DEGREES, RADIANS, EXP") }; static GnmValue * gnumeric_sinh (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (sinhgnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_sqrt = { N_("@FUNCTION=SQRT\n" "@SYNTAX=SQRT(x)\n" "@DESCRIPTION=" "SQRT function returns the square root of @x.\n" "\n" "* If @x is negative, SQRT returns #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "SQRT(2) equals 1.4142136.\n" "\n" "@SEEALSO=POWER") }; static GnmValue * gnumeric_sqrt (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float x = value_get_as_float (argv[0]); if (x < 0) return value_new_error_NUM (ei->pos); return value_new_float (sqrtgnum (x)); } /***************************************************************************/ static char const *help_suma = { N_("@FUNCTION=SUMA\n" "@SYNTAX=SUMA(value1, value2, ...)\n" "@DESCRIPTION=" "SUMA computes the sum of all the values and cells referenced " "in the argument list. Numbers, text and logical values are " "included in the calculation too. If the cell contains text or " "the argument evaluates to FALSE, it is counted as value zero (0). " "If the argument evaluates to TRUE, it is counted as one (1).\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "11, 15, 17, 21, and 43. Then\n" "SUMA(A1:A5) equals 107.\n" "\n" "@SEEALSO=AVERAGE, SUM, COUNT") }; static GnmValue * gnumeric_suma (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_sum, COLLECT_ZERO_STRINGS | COLLECT_ZEROONE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_VALUE); } /***************************************************************************/ static char const *help_sumsq = { N_("@FUNCTION=SUMSQ\n" "@SYNTAX=SUMSQ(value1, value2, ...)\n" "@DESCRIPTION=" "SUMSQ returns the sum of the squares of all the values and " "cells referenced in the argument list.\n\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "11, 15, 17, 21, and 43. Then\n" "SUMSQ(A1:A5) equals 2925.\n" "\n" "@SEEALSO=SUM, COUNT") }; static GnmValue * gnumeric_sumsq (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_sumsq, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_VALUE); } /***************************************************************************/ static char const *help_multinomial = { N_("@FUNCTION=MULTINOMIAL\n" "@SYNTAX=MULTINOMIAL(value1, value2, ...)\n" "@DESCRIPTION=" "MULTINOMIAL returns the ratio of the factorial of a sum of " "values to the product of factorials.\n\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "MULTINOMIAL(2,3,4) equals 1260.\n" "\n" "@SEEALSO=SUM") }; static GnmValue * gnumeric_multinomial (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_multinomial, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_NUM); } /***************************************************************************/ static char const *help_g_product = { N_("@FUNCTION=G_PRODUCT\n" "@SYNTAX=G_PRODUCT(value1, value2, ...)\n" "@DESCRIPTION=" "G_PRODUCT returns the product of all the values and cells " "referenced in the argument list.\n\n" "* Empty cells are ignored and the empty product is 1.\n" "\n" "@EXAMPLES=\n" "G_PRODUCT(2,5,9) equals 90.\n" "\n" "@SEEALSO=SUM, COUNT") }; static GnmValue * gnumeric_g_product (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_product, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_VALUE); } /***************************************************************************/ static char const *help_tan = { N_("@FUNCTION=TAN\n" "@SYNTAX=TAN(x)\n" "@DESCRIPTION=" "TAN function returns the tangent of @x, where @x is " "given in radians.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "TAN(3) equals -0.1425465.\n" "\n" "@SEEALSO=TANH, COS, COSH, SIN, SINH, DEGREES, RADIANS") }; static GnmValue * gnumeric_tan (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (tangnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_tanh = { N_("@FUNCTION=TANH\n" "@SYNTAX=TANH(x)\n" "@DESCRIPTION=" "TANH function returns the hyperbolic tangent of @x, " "which is defined mathematically as \n\n\tsinh(@x) / cosh(@x).\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "TANH(2) equals 0.96402758.\n" "\n" "@SEEALSO=TAN, SIN, SINH, COS, COSH, DEGREES, RADIANS") }; static GnmValue * gnumeric_tanh (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (tanhgnum (value_get_as_float (argv [0]))); } /***************************************************************************/ static char const *help_pi = { N_("@FUNCTION=PI\n" "@SYNTAX=PI()\n" "@DESCRIPTION=" "PI functions returns the value of pi.\n" "\n" "* This function is called with no arguments.\n" "* This function is Excel compatible, except that " "it returns pi with a better precision.\n" "\n" "@EXAMPLES=\n" "PI() equals about 3.141593.\n" "\n" "@SEEALSO=SQRTPI") }; static GnmValue * gnumeric_pi (FunctionEvalInfo *ei, GnmValue **argv) { return value_new_float (M_PIgnum); } /***************************************************************************/ static char const *help_trunc = { N_("@FUNCTION=TRUNC\n" "@SYNTAX=TRUNC(number[,digits])\n" "@DESCRIPTION=" "TRUNC function returns the value of @number " "truncated to the number of digits specified.\n\n" "* If @digits is omitted or negative then @digits defaults to zero.\n" "* If @digits is not an integer, it is truncated.\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "TRUNC(3.12) equals 3.\n" "TRUNC(4.15,1) equals 4.1.\n" "\n" "@SEEALSO=INT") }; static GnmValue * gnumeric_trunc (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, p10; int digits; number = value_get_as_float (argv[0]); if (argv[1] == NULL) digits = 0; else digits = value_get_as_int (argv[1]); p10 = gpow10 (digits); return value_new_float (gnumeric_fake_trunc (number * p10) / p10); } /***************************************************************************/ static char const *help_even = { N_("@FUNCTION=EVEN\n" "@SYNTAX=EVEN(number)\n" "@DESCRIPTION=" "EVEN function returns the number rounded up to the " "nearest even integer.\n\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "EVEN(5.4) equals 6.\n" "\n" "@SEEALSO=ODD") }; static GnmValue * gnumeric_even (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, ceiled; int sign = 1; number = value_get_as_float (argv[0]); if (number < 0) { sign = -1; number = -number; } ceiled = ceilgnum (number); if (fmodgnum (ceiled, 2) == 0) if (number > ceiled) return value_new_int ((int) (sign * (ceiled + 2))); else return value_new_int ((int) (sign * ceiled)); else return value_new_int ((int) (sign * (ceiled + 1))); } /***************************************************************************/ static char const *help_odd = { N_("@FUNCTION=ODD\n" "@SYNTAX=ODD(number)\n" "@DESCRIPTION=" "ODD function returns the @number rounded up to the " "nearest odd integer.\n\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "ODD(4.4) equals 5.\n" "\n" "@SEEALSO=EVEN") }; static GnmValue * gnumeric_odd (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, ceiled; int sign = 1; number = value_get_as_float (argv[0]); if (number < 0) { sign = -1; number = -number; } ceiled = ceilgnum (number); if (fmodgnum (ceiled, 2) == 1) if (number > ceiled) return value_new_int ((int) (sign * (ceiled + 2))); else return value_new_int ((int) (sign * ceiled)); else return value_new_int ((int) (sign * (ceiled + 1))); } /***************************************************************************/ static char const *help_factdouble = { N_("@FUNCTION=FACTDOUBLE\n" "@SYNTAX=FACTDOUBLE(number)\n" "@DESCRIPTION=" "FACTDOUBLE function returns the double factorial " "of a @number, i.e., x!!.\n" "\n" "* If @number is not an integer, it is truncated.\n" "* If @number is negative FACTDOUBLE returns #NUM! error.\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "FACTDOUBLE(5) equals 15.\n" "\n" "@SEEALSO=FACT") }; static GnmValue * gnumeric_factdouble (FunctionEvalInfo *ei, GnmValue **argv) { int number; int n; gnm_float product = 1; number = value_get_as_int (argv[0]); if (number < 0) return value_new_error_NUM (ei->pos); for (n = number; n > 0; n -= 2) product *= n; return value_new_float (product); } /***************************************************************************/ static char const *help_fib = { N_("@FUNCTION=FIB\n" "@SYNTAX=FIB(number)\n" "@DESCRIPTION=" "FIB function computes Fibonacci numbers.\n" "\n" "* If @number is not an integer, it is truncated.\n" "* If @number is negative or zero FIB returns #NUM! error.\n" "\n" "@EXAMPLES=\n" "FIB(12) equals 144.\n" "\n" "@SEEALSO=") }; static GnmValue * gnumeric_fib (FunctionEvalInfo *ei, GnmValue **argv) { static int fibs[47]; static int fib_count = G_N_ELEMENTS (fibs); static gboolean inited = FALSE; int n = value_get_as_int (argv[0]); if (n <= 0) return value_new_error_NUM (ei->pos); if (n < fib_count) { if (!inited) { int i; fibs[1] = fibs[2] = 1; for (i = 3; i < fib_count; i++) fibs[i] = fibs[i - 1] + fibs[i - 2]; inited = TRUE; } return value_new_int (fibs[n]); } else { gnm_float s5 = sqrtgnum (5.0); gnm_float r1 = (1 + s5) / 2; gnm_float r2 = (1 - s5) / 2; /* Use the Binet form. */ return value_new_float ((powgnum (r1, n) - powgnum (r2, n)) / s5); } } /***************************************************************************/ static char const *help_quotient = { N_("@FUNCTION=QUOTIENT\n" "@SYNTAX=QUOTIENT(numerator,denominator)\n" "@DESCRIPTION=" "QUOTIENT function returns the integer portion " "of a division. @numerator is the divided number and " "@denominator is the divisor.\n\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "QUOTIENT(23,5) equals 4.\n" "\n" "@SEEALSO=MOD") }; static GnmValue * gnumeric_quotient (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float num, den; num = value_get_as_float (argv[0]); den = value_get_as_float (argv[1]); if (den == 0) return value_new_error_DIV0 (ei->pos); else return value_new_int ((int) (num / den)); } /***************************************************************************/ static char const *help_sign = { N_("@FUNCTION=SIGN\n" "@SYNTAX=SIGN(number)\n" "@DESCRIPTION=" "SIGN function returns 1 if the @number is positive, " "zero if the @number is 0, and -1 if the @number is negative.\n\n" "* This function is Excel compatible.\n " "\n" "@EXAMPLES=\n" "SIGN(3) equals 1.\n" "SIGN(-3) equals -1.\n" "SIGN(0) equals 0.\n" "\n" "@SEEALSO=") }; static GnmValue * gnumeric_sign (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float n; n = value_get_as_float (argv[0]); if (n > 0) return value_new_int (1); else if (n == 0) return value_new_int (0); else return value_new_int (-1); } /***************************************************************************/ static char const *help_sqrtpi = { N_("@FUNCTION=SQRTPI\n" "@SYNTAX=SQRTPI(number)\n" "@DESCRIPTION=" "SQRTPI function returns the square root of a @number " "multiplied by pi.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "SQRTPI(2) equals 2.506628275.\n" "\n" "@SEEALSO=PI") }; static GnmValue * gnumeric_sqrtpi (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float n; n = value_get_as_float (argv[0]); if (n < 0) return value_new_error_NUM (ei->pos); return value_new_float (sqrtgnum (M_PIgnum * n)); } /***************************************************************************/ static char const *help_rounddown = { N_("@FUNCTION=ROUNDDOWN\n" "@SYNTAX=ROUNDDOWN(number[,digits])\n" "@DESCRIPTION=" "ROUNDDOWN function rounds a given @number down. " "@number is the number you want rounded down and @digits is the " "number of digits to which you want to round that number.\n" "\n" "* If @digits is greater than zero, @number is rounded down to the " "given number of digits.\n" "* If @digits is zero or omitted, @number is rounded down to the " "nearest integer.\n" "* If @digits is less than zero, @number is rounded down to the " "left of the decimal point.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ROUNDDOWN(5.5) equals 5.\n" "ROUNDDOWN(-3.3) equals -4.\n" "ROUNDDOWN(1501.15,1) equals 1501.1.\n" "ROUNDDOWN(1501.15,-2) equals 1500.0.\n" "\n" "@SEEALSO=ROUND,ROUNDUP") }; static GnmValue * gnumeric_rounddown (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, p10; int digits; number = value_get_as_float (argv[0]); if (argv[1] == NULL) digits = 0; else digits = value_get_as_int (argv[1]); p10 = gpow10 (digits); return value_new_float (gnumeric_fake_floor (number * p10) / p10); } /***************************************************************************/ static char const *help_round = { N_("@FUNCTION=ROUND\n" "@SYNTAX=ROUND(number[,digits])\n" "@DESCRIPTION=" "ROUND function rounds a given number.\n\n" "@number is the number you want rounded and @digits is the " "number of digits to which you want to round that number.\n" "\n" "* If @digits is greater than zero, @number is rounded to the " "given number of digits.\n" "* If @digits is zero or omitted, @number is rounded to the " "nearest integer.\n" "* If @digits is less than zero, @number is rounded to the left " "of the decimal point.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ROUND(5.5) equals 6.\n" "ROUND(-3.3) equals -3.\n" "ROUND(1501.15,1) equals 1501.2.\n" "ROUND(1501.15,-2) equals 1500.0.\n" "\n" "@SEEALSO=ROUNDDOWN,ROUNDUP") }; static GnmValue * gnumeric_round (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, p10; int digits; number = value_get_as_float (argv[0]); digits = argv[1] ? value_get_as_int (argv[1]) : 0; p10 = gpow10 (digits); return value_new_float (gnumeric_fake_round (number * p10) / p10); } /***************************************************************************/ static char const *help_roundup = { N_("@FUNCTION=ROUNDUP\n" "@SYNTAX=ROUNDUP(number[,digits])\n" "@DESCRIPTION=" "ROUNDUP function rounds a given number up.\n\n" "@number is the number you want rounded up and @digits is the " "number of digits to which you want to round that number.\n" "\n" "* If @digits is greater than zero, @number is rounded up to the " "given number of digits.\n" "* If @digits is zero or omitted, @number is rounded up to the " "nearest integer.\n" "* If @digits is less than zero, @number is rounded up to the left " "of the decimal point.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ROUNDUP(5.5) equals 6.\n" "ROUNDUP(-3.3) equals -3.\n" "ROUNDUP(1501.15,1) equals 1501.2.\n" "ROUNDUP(1501.15,-2) equals 1600.0.\n" "\n" "@SEEALSO=ROUND,ROUNDDOWN") }; static GnmValue * gnumeric_roundup (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float number, p10; int digits; number = value_get_as_float (argv[0]); if (argv[1] == NULL) digits = 0; else digits = value_get_as_int (argv[1]); p10 = gpow10 (digits); return value_new_float (gnumeric_fake_ceil (number * p10) / p10); } /***************************************************************************/ static char const *help_mround = { N_("@FUNCTION=MROUND\n" "@SYNTAX=MROUND(number,multiple)\n" "@DESCRIPTION=" "MROUND function rounds a given number to the desired multiple.\n\n" "@number is the number you want rounded and @multiple is the " "the multiple to which you want to round the number.\n" "\n" "* If @number and @multiple have different sign, MROUND " "returns #NUM! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "MROUND(1.7,0.2) equals 1.8.\n" "MROUND(321.123,0.12) equals 321.12.\n" "\n" "@SEEALSO=ROUNDDOWN,ROUND,ROUNDUP") }; static GnmValue * gnumeric_mround (FunctionEvalInfo *ei, GnmValue **argv) { gnm_float const accuracy_limit = 0.0000003; gnm_float number, multiple; gnm_float div, mod; int sign = 1; number = value_get_as_float (argv[0]); multiple = value_get_as_float (argv[1]); /* Weird, but XL compatible. */ if (multiple == 0) return value_new_int (0); if ((number > 0 && multiple < 0) || (number < 0 && multiple > 0)) return value_new_error_NUM (ei->pos); if (number < 0) { sign = -1; number = -number; multiple = -multiple; } mod = fmodgnum (number, multiple); div = number - mod; return value_new_float (sign * ( div + ((mod + accuracy_limit >= multiple / 2) ? multiple : 0))); } /***************************************************************************/ static char const *help_roman = { N_("@FUNCTION=ROMAN\n" "@SYNTAX=ROMAN(number[,type])\n" "@DESCRIPTION=" "ROMAN function returns an arabic number in the roman numeral " "style, as text. @number is the number you want to convert and " "@type is the type of roman numeral you want.\n" "\n" "* If @type is 0 or it is omitted, ROMAN returns classic roman " "numbers.\n" "* Type 1 is more concise than classic type, type 2 is more concise " "than type 1, and type 3 is more concise than type 2. Type 4 " "is simplified type." "\n" "* If @number is negative or greater than 3999, ROMAN returns " "#VALUE! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "ROMAN(999) equals CMXCIX.\n" "ROMAN(999,1) equals LMVLIV.\n" "ROMAN(999,2) equals XMIX.\n" "ROMAN(999,3) equals VMIV.\n" "ROMAN(999,4) equals IM.\n" "\n" "@SEEALSO=") }; static GnmValue * gnumeric_roman (FunctionEvalInfo *ei, GnmValue **argv) { char const letter[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' }; int const largest = 1000; static char buf[256]; char *p; int n, form; int i, j, dec; dec = largest; n = value_get_as_int (argv[0]); form = argv[1] ? value_get_as_int (argv[1]) : 0; if (n < 0 || n > 3999) return value_new_error_VALUE (ei->pos); if (n == 0) return value_new_string (""); if (form < 0 || form > 4) return value_new_error_NUM (ei->pos); for (i = j = 0; dec > 1; dec /= 10, j += 2) { for (; n > 0; i++) { if (n >= dec) { buf[i] = letter [j]; n -= dec; } else if (n >= dec - dec / 10) { buf [i++] = letter [j + 2]; buf [i] = letter [j]; n -= dec - dec / 10; } else if (n >= dec / 2) { buf [i] = letter [j + 1]; n -= dec / 2; } else if (n >= dec / 2 - dec / 10) { buf [i++] = letter [j + 2]; buf [i] = letter [j + 1]; n -= dec / 2 - dec / 10; } else if (dec == 10) { buf [i] = letter [j + 2]; n--; } else break; } } buf [i] = '\0'; if (form > 0) { /* Replace ``XLV'' with ``VL'' */ if ((p = strstr (buf, "XLV")) != NULL) { *p++ = 'V'; *p++ = 'L'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``XCV'' with ``VC'' */ if ((p = strstr (buf, "XCV")) != NULL) { *p++ = 'V'; *p++ = 'C'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``CDL'' with ``LD'' */ if ((p = strstr (buf, "CDL")) != NULL) { *p++ = 'L'; *p++ = 'D'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``CML'' with ``LM'' */ if ((p = strstr (buf, "CML")) != NULL) { *p++ = 'L'; *p++ = 'M'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``CMVC'' with ``LMVL'' */ if ((p = strstr (buf, "CMVC")) != NULL) { *p++ = 'L'; *p++ = 'M'; *p++ = 'V'; *p++ = 'L'; } } if (form == 1) { /* Replace ``CDXC'' with ``LDXL'' */ if ((p = strstr (buf, "CDXC")) != NULL) { *p++ = 'L'; *p++ = 'D'; *p++ = 'X'; *p++ = 'L'; } /* Replace ``CDVC'' with ``LDVL'' */ if ((p = strstr (buf, "CDVC")) != NULL) { *p++ = 'L'; *p++ = 'D'; *p++ = 'V'; *p++ = 'L'; } /* Replace ``CMXC'' with ``LMXL'' */ if ((p = strstr (buf, "CMXC")) != NULL) { *p++ = 'L'; *p++ = 'M'; *p++ = 'X'; *p++ = 'L'; } /* Replace ``XCIX'' with ``VCIV'' */ if ((p = strstr (buf, "XCIX")) != NULL) { *p++ = 'V'; *p++ = 'C'; *p++ = 'I'; *p++ = 'V'; } /* Replace ``XLIX'' with ``VLIV'' */ if ((p = strstr (buf, "XLIX")) != NULL) { *p++ = 'V'; *p++ = 'L'; *p++ = 'I'; *p++ = 'V'; } } if (form > 1) { /* Replace ``XLIX'' with ``IL'' */ if ((p = strstr (buf, "XLIX")) != NULL) { *p++ = 'I'; *p++ = 'L'; for ( ; *p; p++) *p = *(p+2); } /* Replace ``XCIX'' with ``IC'' */ if ((p = strstr (buf, "XCIX")) != NULL) { *p++ = 'I'; *p++ = 'C'; for ( ; *p; p++) *p = *(p+2); } /* Replace ``CDXC'' with ``XD'' */ if ((p = strstr (buf, "CDXC")) != NULL) { *p++ = 'X'; *p++ = 'D'; for ( ; *p; p++) *p = *(p+2); } /* Replace ``CDVC'' with ``XDV'' */ if ((p = strstr (buf, "CDVC")) != NULL) { *p++ = 'X'; *p++ = 'D'; *p++ = 'V'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``CDIC'' with ``XDIX'' */ if ((p = strstr (buf, "CDIC")) != NULL) { *p++ = 'X'; *p++ = 'D'; *p++ = 'I'; *p++ = 'X'; } /* Replace ``LMVL'' with ``XMV'' */ if ((p = strstr (buf, "LMVL")) != NULL) { *p++ = 'X'; *p++ = 'M'; *p++ = 'V'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``CMIC'' with ``XMIX'' */ if ((p = strstr (buf, "CMIC")) != NULL) { *p++ = 'X'; *p++ = 'M'; *p++ = 'I'; *p++ = 'X'; } /* Replace ``CMXC'' with ``XM'' */ if ((p = strstr (buf, "CMXC")) != NULL) { *p++ = 'X'; *p++ = 'M'; for ( ; *p; p++) *p = *(p+2); } } if (form > 2) { /* Replace ``XDV'' with ``VD'' */ if ((p = strstr (buf, "XDV")) != NULL) { *p++ = 'V'; *p++ = 'D'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``XDIX'' with ``VDIV'' */ if ((p = strstr (buf, "XDIX")) != NULL) { *p++ = 'V'; *p++ = 'D'; *p++ = 'I'; *p++ = 'V'; } /* Replace ``XMV'' with ``VM'' */ if ((p = strstr (buf, "XMV")) != NULL) { *p++ = 'V'; *p++ = 'M'; for ( ; *p; p++) *p = *(p+1); } /* Replace ``XMIX'' with ``VMIV'' */ if ((p = strstr (buf, "XMIX")) != NULL) { *p++ = 'V'; *p++ = 'M'; *p++ = 'I'; *p++ = 'V'; } } if (form == 4) { /* Replace ``VDIV'' with ``ID'' */ if ((p = strstr (buf, "VDIV")) != NULL) { *p++ = 'I'; *p++ = 'D'; for ( ; *p; p++) *p = *(p+2); } /* Replace ``VMIV'' with ``IM'' */ if ((p = strstr (buf, "VMIV")) != NULL) { *p++ = 'I'; *p++ = 'M'; for ( ; *p; p++) *p = *(p+2); } } return value_new_string (buf); } /***************************************************************************/ static char const *help_sumx2my2 = { N_("@FUNCTION=SUMX2MY2\n" "@SYNTAX=SUMX2MY2(array1,array2)\n" "@DESCRIPTION=" "SUMX2MY2 function returns the sum of the difference of squares " "of corresponding values in two arrays. @array1 is the first " "array or range of data points and @array2 is the second array " "or range of data points. The equation of SUMX2MY2 is " "SUM (x^2-y^2).\n" "\n" "* Strings and empty cells are simply ignored.\n" "* If @array1 and @array2 have different number of data points, " "SUMX2MY2 returns #N/A error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "11, 15, 17, 21, and 43 and the cells B1, B2, ..., B5 hold " "numbers 13, 22, 31, 33, and 39. Then\n" "SUMX2MY2(A1:A5,B1:B5) equals -1299.\n" "\n" "@SEEALSO=SUMSQ,SUMX2PY2") }; static GnmValue * gnumeric_sumx2my2 (FunctionEvalInfo *ei, GnmValue **argv) { GnmValue *values_x = argv[0]; GnmValue *values_y = argv[1]; math_sums_t items_x, items_y; GnmValue *ret; gnm_float sum; GSList *list1, *list2; items_x.num = 0; items_x.list = NULL; items_y.num = 0; items_y.list = NULL; if (values_x->type == VALUE_CELLRANGE) { ret = sheet_foreach_cell_in_range ( eval_sheet (ei->pos->sheet, ei->pos->sheet), CELL_ITER_ALL, values_x->v_range.cell.a.col, values_x->v_range.cell.a.row, values_x->v_range.cell.b.col, values_x->v_range.cell.b.row, callback_function_sumxy, &items_x); if (ret != NULL) { ret = value_new_error_VALUE (ei->pos); goto out; } } else { ret = value_new_error (ei->pos, _("Array version not implemented!")); goto out; } if (values_y->type == VALUE_CELLRANGE) { ret = sheet_foreach_cell_in_range ( eval_sheet (ei->pos->sheet, ei->pos->sheet), CELL_ITER_ALL, values_y->v_range.cell.a.col, values_y->v_range.cell.a.row, values_y->v_range.cell.b.col, values_y->v_range.cell.b.row, callback_function_sumxy, &items_y); if (ret != NULL) { ret = value_new_error_VALUE (ei->pos); goto out; } } else { ret = value_new_error (ei->pos, _("Array version not implemented!")); goto out; } if (items_x.num != items_y.num) { ret = value_new_error_NA (ei->pos); goto out; } list1 = items_x.list; list2 = items_y.list; sum = 0; while (list1 != NULL) { gnm_float x, y; x = *((gnm_float *) list1->data); y = *((gnm_float *) list2->data); sum += x * x - y * y; list1 = list1->next; list2 = list2->next; } ret = value_new_float (sum); out: for (list1 = items_x.list; list1; list1 = list1->next) g_free (list1->data); g_slist_free (items_x.list); for (list2 = items_y.list; list2; list2 = list2->next) g_free (list2->data); g_slist_free (items_y.list); return ret; } /***************************************************************************/ static char const *help_sumx2py2 = { N_("@FUNCTION=SUMX2PY2\n" "@SYNTAX=SUMX2PY2(array1,array2)\n" "@DESCRIPTION=" "SUMX2PY2 function returns the sum of the sum of squares " "of corresponding values in two arrays. @array1 is the first " "array or range of data points and @array2 is the second array " "or range of data points. The equation of SUMX2PY2 is " "SUM (x^2+y^2).\n" "\n" "* Strings and empty cells are simply ignored.\n" "* If @array1 and @array2 have different number of data points, " "SUMX2PY2 returns #N/A error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "11, 15, 17, 21, and 43 and the cells B1, B2, ..., B5 hold " "numbers 13, 22, 31, 33, and 39. Then\n" "SUMX2PY2(A1:A5,B1:B5) equals 7149.\n" "\n" "@SEEALSO=SUMSQ,SUMX2MY2") }; static GnmValue * gnumeric_sumx2py2 (FunctionEvalInfo *ei, GnmValue **argv) { GnmValue *values_x = argv[0]; GnmValue *values_y = argv[1]; math_sums_t items_x, items_y; GnmValue *ret; gnm_float sum; GSList *list1, *list2; items_x.num = 0; items_x.list = NULL; items_y.num = 0; items_y.list = NULL; if (values_x->type == VALUE_CELLRANGE) { ret = sheet_foreach_cell_in_range ( eval_sheet (ei->pos->sheet, ei->pos->sheet), CELL_ITER_ALL, /* include empties so that the lists align */ values_x->v_range.cell.a.col, values_x->v_range.cell.a.row, values_x->v_range.cell.b.col, values_x->v_range.cell.b.row, callback_function_sumxy, &items_x); if (ret != NULL) { ret = value_new_error_VALUE (ei->pos); goto out; } } else { ret = value_new_error (ei->pos, _("Array version not implemented!")); goto out; } if (values_y->type == VALUE_CELLRANGE) { ret = sheet_foreach_cell_in_range ( eval_sheet (ei->pos->sheet, ei->pos->sheet), CELL_ITER_ALL, /* include empties so that the lists align */ values_y->v_range.cell.a.col, values_y->v_range.cell.a.row, values_y->v_range.cell.b.col, values_y->v_range.cell.b.row, callback_function_sumxy, &items_y); if (ret != NULL) { ret = value_new_error_VALUE (ei->pos); goto out; } } else { ret = value_new_error (ei->pos, _("Array version not implemented!")); goto out; } if (items_x.num != items_y.num) { ret = value_new_error_NA (ei->pos); goto out; } list1 = items_x.list; list2 = items_y.list; sum = 0; while (list1 != NULL) { gnm_float x, y; x = *((gnm_float *) list1->data); y = *((gnm_float *) list2->data); sum += x * x + y * y; list1 = list1->next; list2 = list2->next; } ret = value_new_float (sum); out: for (list1 = items_x.list; list1; list1 = list1->next) g_free (list1->data); g_slist_free (items_x.list); for (list2 = items_y.list; list2; list2 = list2->next) g_free (list2->data); g_slist_free (items_y.list); return ret; } static char const *help_sumxmy2 = { N_("@FUNCTION=SUMXMY2\n" "@SYNTAX=SUMXMY2(array1,array2)\n" "@DESCRIPTION=" "SUMXMY2 function returns the sum of squares of differences " "of corresponding values in two arrays. @array1 is the first " "array or range of data points and @array2 is the second array " "or range of data points. The equation of SUMXMY2 is " "SUM (x-y)^2.\n" "\n" "* Strings and empty cells are simply ignored.\n" "* If @array1 and @array2 have different number of data points, " "SUMXMY2 returns #N/A error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "11, 15, 17, 21, and 43 and the cells B1, B2, ..., B5 hold " "numbers 13, 22, 31, 33, and 39. Then\n" "SUMXMY2(A1:A5,B1:B5) equals 409.\n" "\n" "@SEEALSO=SUMSQ,SUMX2MY2,SUMX2PY2") }; static GnmValue * gnumeric_sumxmy2 (FunctionEvalInfo *ei, GnmValue **argv) { GnmValue *values_x = argv[0]; GnmValue *values_y = argv[1]; math_sums_t items_x, items_y; GnmValue *ret; gnm_float sum; GSList *list1, *list2; items_x.num = 0; items_x.list = NULL; items_y.num = 0; items_y.list = NULL; if (values_x->type == VALUE_CELLRANGE) { ret = sheet_foreach_cell_in_range ( eval_sheet (ei->pos->sheet, ei->pos->sheet), CELL_ITER_ALL, /* include empties so that the lists align */ values_x->v_range.cell.a.col, values_x->v_range.cell.a.row, values_x->v_range.cell.b.col, values_x->v_range.cell.b.row, callback_function_sumxy, &items_x); if (ret != NULL) { ret = value_new_error_VALUE (ei->pos); goto out; } } else { ret = value_new_error (ei->pos, _("Array version not implemented!")); goto out; } if (values_y->type == VALUE_CELLRANGE) { ret = sheet_foreach_cell_in_range ( eval_sheet (ei->pos->sheet, ei->pos->sheet), CELL_ITER_ALL, /* include empties so that the lists align */ values_y->v_range.cell.a.col, values_y->v_range.cell.a.row, values_y->v_range.cell.b.col, values_y->v_range.cell.b.row, callback_function_sumxy, &items_y); if (ret != NULL) { ret = value_new_error_VALUE (ei->pos); goto out; } } else { ret = value_new_error (ei->pos, _("Array version not implemented!")); goto out; } if (items_x.num != items_y.num) { ret = value_new_error_NA (ei->pos); goto out; } list1 = items_x.list; list2 = items_y.list; sum = 0; while (list1 != NULL) { gnm_float x, y; x = *((gnm_float *) list1->data); y = *((gnm_float *) list2->data); sum += (x - y) * (x - y); list1 = list1->next; list2 = list2->next; } ret = value_new_float (sum); out: for (list1 = items_x.list; list1; list1 = list1->next) g_free (list1->data); g_slist_free (items_x.list); for (list2 = items_y.list; list2; list2 = list2->next) g_free (list2->data); g_slist_free (items_y.list); return ret; } /***************************************************************************/ static char const *help_seriessum = { N_("@FUNCTION=SERIESSUM\n" "@SYNTAX=SERIESSUM(x,n,m,coefficients)\n" "@DESCRIPTION=" "SERIESSUM function returns the sum of a power series. @x is " "the base of the power series, @n is the initial power to raise @x, " "@m is the increment to the power for each term in the series, and " "@coefficients are the coefficients by which each successive power " "of @x is multiplied.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "1.23, 2.32, 2.98, 3.42, and 4.33. Then\n" "SERIESSUM(3,1,2.23,A1:A5) equals 251416.43018.\n" "\n" "@SEEALSO=COUNT,SUM") }; static int range_seriessum (gnm_float const *xs, int n, gnm_float *res) { if (n >= 3) { gnm_float x = xs[0]; gnm_float N = xs[1]; gnm_float m = xs[2]; gnm_float sum = 0; gnm_float x_m = powgnum (x, m); gnm_float xpow = powgnum (x, N); int i; for (i = 3; i < n; i++) { sum += xs[i] * xpow; xpow *= x_m; } *res = sum; return 0; } else return 1; } static GnmValue * gnumeric_seriessum (FunctionEvalInfo *ei, GnmExprList *nodes) { return float_range_function (nodes, ei, range_seriessum, COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, GNM_ERROR_NUM); } /***************************************************************************/ static char const *help_minverse = { N_("@FUNCTION=MINVERSE\n" "@SYNTAX=MINVERSE(matrix)\n" "@DESCRIPTION=" "MINVERSE function returns the inverse matrix of @matrix.\n" "\n" "* If @matrix cannot be inverted, MINVERSE returns #NUM! " "error.\n" "* If @matrix does not contain equal number of columns and " "rows, MINVERSE returns #VALUE! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "\n" "@SEEALSO=MMULT, MDETERM") }; static GnmValue * callback_function_mmult_validate (Sheet *sheet, int col, int row, GnmCell *cell, void *user_data) { int *item_count = user_data; cell_eval (cell); if (!VALUE_IS_NUMBER (cell->value)) return VALUE_TERMINATE; ++(*item_count); return NULL; } static gboolean validate_range_numeric_matrix (GnmEvalPos const *ep, GnmValue * matrix, int *rows, int *cols, GnmStdError *err) { GnmValue *res; int cell_count = 0; *cols = value_area_get_width (matrix, ep); *rows = value_area_get_height (matrix, ep); /* No checking needed for arrays */ if (matrix->type == VALUE_ARRAY) return FALSE; if (matrix->v_range.cell.a.sheet != matrix->v_range.cell.b.sheet && matrix->v_range.cell.a.sheet != NULL && matrix->v_range.cell.b.sheet != NULL) { *err = GNM_ERROR_VALUE; return TRUE; } res = sheet_foreach_cell_in_range ( eval_sheet (matrix->v_range.cell.a.sheet, ep->sheet), CELL_ITER_IGNORE_BLANK, matrix->v_range.cell.a.col, matrix->v_range.cell.a.row, matrix->v_range.cell.b.col, matrix->v_range.cell.b.row, callback_function_mmult_validate, &cell_count); if (res != NULL || cell_count != (*rows * *cols)) { /* As specified in the Excel Docs */ *err = GNM_ERROR_VALUE; return TRUE; } return FALSE; } static gnm_float ** value_to_matrix (GnmValue const *v, int cols, int rows, GnmEvalPos const *ep) { gnm_float **res = g_new (gnm_float *, rows); int r, c; for (r = 0; r < rows; r++) { res[r] = g_new (gnm_float, cols); for (c = 0; c < cols; c++) res[r][c] = value_get_as_float (value_area_get_x_y (v, c, r, ep)); } return res; } static void free_matrix (gnm_float **mat, G_GNUC_UNUSED int cols, int rows) { int r; for (r = 0; r < rows; r++) g_free (mat[r]); g_free (mat); } static GnmValue * gnumeric_minverse (FunctionEvalInfo *ei, GnmValue **argv) { GnmEvalPos const * const ep = ei->pos; int r, rows; int c, cols; GnmValue *res; GnmValue *values = argv[0]; gnm_float **matrix; GnmStdError err; if (validate_range_numeric_matrix (ep, values, &rows, &cols, &err)) { return value_new_error_std (ei->pos, err); } /* Guarantee shape and non-zero size */ if (cols != rows || !rows || !cols) return value_new_error_VALUE (ei->pos); matrix = value_to_matrix (values, cols, rows, ep); if (!matrix_invert (matrix, rows)) { free_matrix (matrix, cols, rows); return value_new_error_NUM (ei->pos); } res = value_new_array_non_init (cols, rows); for (c = 0; c < cols; ++c) { res->v_array.vals[c] = g_new (GnmValue *, rows); for (r = 0; r < rows; ++r) { gnm_float tmp = matrix[r][c]; res->v_array.vals[c][r] = value_new_float (tmp); } } free_matrix (matrix, cols, rows); return res; } /***************************************************************************/ static char const *help_mmult = { N_("@FUNCTION=MMULT\n" "@SYNTAX=MMULT(array1,array2)\n" "@DESCRIPTION=" "MMULT function returns the matrix product of two arrays. The " "result is an array with the same number of rows as @array1 and " "the same number of columns as @array2.\n\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "\n" "@SEEALSO=TRANSPOSE,MINVERSE") }; static GnmValue * gnumeric_mmult (FunctionEvalInfo *ei, GnmValue **argv) { GnmEvalPos const * const ep = ei->pos; int r, rows_a, rows_b; int c, cols_a, cols_b; GnmValue *res; GnmValue *values_a = argv[0]; GnmValue *values_b = argv[1]; gnm_float *A, *B, *product; GnmStdError err; if (validate_range_numeric_matrix (ep, values_a, &rows_a, &cols_a, &err) || validate_range_numeric_matrix (ep, values_b, &rows_b, &cols_b, &err)) { return value_new_error_std (ei->pos, err); } /* Guarantee shape and non-zero size */ if (cols_a != rows_b || !rows_a || !rows_b || !cols_a || !cols_b) return value_new_error_VALUE (ei->pos); res = value_new_array_non_init (cols_b, rows_a); A = g_new (gnm_float, cols_a * rows_a); B = g_new (gnm_float, cols_b * rows_b); product = g_new (gnm_float, rows_a * cols_b); for (c = 0; c < cols_a; c++) for (r = 0; r < rows_a; r++) { GnmValue const * a = value_area_get_x_y (values_a, c, r, ep); A[r + c * rows_a] = value_get_as_float (a); } for (c = 0; c < cols_b; c++) for (r = 0; r < rows_b; r++) { GnmValue const * b = value_area_get_x_y (values_b, c, r, ep); B[r + c * rows_b] = value_get_as_float (b); } mmult (A, B, cols_a, rows_a, cols_b, product); for (c = 0; c < cols_b; c++) { res->v_array.vals[c] = g_new (GnmValue *, rows_a); for (r = 0; r < rows_a; r++) res->v_array.vals[c][r] = value_new_float (product [r + c * rows_a]); } g_free (A); g_free (B); g_free (product); return res; } /***************************************************************************/ static char const *help_mdeterm = { N_("@FUNCTION=MDETERM\n" "@SYNTAX=MDETERM(matrix)\n" "@DESCRIPTION=" "MDETERM function returns the determinant of a given matrix.\n" "\n" "* If the @matrix does not contain equal number of columns and " "rows, MDETERM returns #VALUE! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that A1, ..., A4 contain numbers 2, 3, 7, and 3, " "B1, ..., B4 4, 2, 4, and 1, C1, ..., C4 9, 4, 3, and 2, and " "D1, ..., D4 7, 3, 6, and 5. Then\n" "MDETERM(A1:D4) equals 148.\n" "\n" "@SEEALSO=MMULT, MINVERSE") }; static GnmValue * gnumeric_mdeterm (FunctionEvalInfo *ei, GnmValue **argv) { GnmEvalPos const * const ep = ei->pos; int rows, cols; gnm_float res; GnmValue *values = argv[0]; gnm_float **matrix; GnmStdError err; if (validate_range_numeric_matrix (ep, values, &rows, &cols, &err)) return value_new_error_std (ei->pos, err); /* Guarantee shape and non-zero size */ if (cols != rows || !rows || !cols) return value_new_error_VALUE (ei->pos); matrix = value_to_matrix (values, cols, rows, ep); res = matrix_determinant (matrix, rows); free_matrix (matrix, cols, rows); return value_new_float (res); } /***************************************************************************/ static char const *help_sumproduct = { N_("@FUNCTION=SUMPRODUCT\n" "@SYNTAX=SUMPRODUCT(range1,range2,...)\n" "@DESCRIPTION=" "SUMPRODUCT function multiplies corresponding data entries in the " "given arrays or ranges, and then returns the sum of those " "products. If an array entry is not numeric, the value zero is " "used instead.\n" "\n" "* If arrays or range arguments do not have the same dimensions, " "SUMPRODUCT returns #VALUE! error.\n" "* This function is Excel compatible.\n" "\n" "@EXAMPLES=\n" "Let us assume that the cells A1, A2, ..., A5 contain numbers " "11, 15, 17, 21, and 43 and the cells B1, B2, ..., B5 hold " "numbers 13, 22, 31, 33, and 39. Then\n" "SUMPRODUCT(A1:A5,B1:B5) equals 3370.\n" "\n" "@SEEALSO=SUM,PRODUCT") }; static GnmValue * gnumeric_sumproduct (FunctionEvalInfo *ei, GnmExprList *args) { gnm_float **data; GnmValue *result; int i, argc; GnmExprList *l; gboolean size_error = FALSE; int sizex = -1, sizey = -1; if (args == NULL) return value_new_error_VALUE (ei->pos); argc = gnm_expr_list_length (args); data = g_new0 (gnm_float *, argc); for (l = args, i = 0; l; l = l->next, i++) { int thissizex, thissizey, x, y; GnmExpr const *expr = l->data; GnmValue *val = gnm_expr_eval (expr, ei->pos, GNM_EXPR_EVAL_PERMIT_NON_SCALAR | GNM_EXPR_EVAL_PERMIT_EMPTY); thissizex = value_area_get_width (val, ei->pos); thissizey = value_area_get_height (val, ei->pos); if (i == 0) { sizex = thissizex; sizey = thissizey; } else if (sizex != thissizex || sizey != thissizey) size_error = TRUE; data[i] = g_new (gnm_float, thissizex * thissizey); for (y = 0; y < thissizey; y++) { for (x = 0; x < thissizex; x++) { /* FIXME: efficiency worries? */ GnmValue const *v = value_area_fetch_x_y (val, x, y, ei->pos); if (v->type == VALUE_ERROR) { /* * We carefully tranverse the argument * list and then the arrays in such an * order that the first error we see is * the final result. * * args: left-to-right. * arrays: horizontal before vertical. * * Oh, size_error has the lowest * significance -- it will be checked * outside the arg loop. */ result = value_dup (v); value_release (val); goto done; } data[i][y * thissizex + x] = value_get_as_float (v); } } value_release (val); } if (size_error) { /* * If we found no errors in the data set and also the sizes * do not match, we will get here. */ result = value_new_error_VALUE (ei->pos); } else { gnm_float sum = 0; int j; for (j = 0; j < sizex * sizey; j++) { gnm_float product = data[0][j]; for (i = 1; i < argc; i++) product *= data[i][j]; sum += product; } result = value_new_float (sum); } done: for (i = 0; i < argc; i++) g_free (data[i]); g_free (data); return result; } /***************************************************************************/ GnmFuncDescriptor const math_functions[] = { { "abs", "f", N_("number"), &help_abs, gnumeric_abs, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "acos", "f", N_("number"), &help_acos, gnumeric_acos, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "acosh", "f", N_("number"), &help_acosh, gnumeric_acosh, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "asin", "f", N_("number"), &help_asin, gnumeric_asin, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "asinh", "f", N_("number"), &help_asinh, gnumeric_asinh, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "atan", "f", N_("number"), &help_atan, gnumeric_atan, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "atanh", "f", N_("number"), &help_atanh, gnumeric_atanh, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "atan2", "ff", N_("xnum,ynum"), &help_atan2, gnumeric_atan2, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "cos", "f", N_("number"), &help_cos, gnumeric_cos, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "beta", "ff", N_("a,b"), &help_beta, gnumeric_beta, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "betaln", "ff", N_("a,b"), &help_betaln, gnumeric_betaln, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "cosh", "f", N_("number"), &help_cosh, gnumeric_cosh, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, /* MS Excel puts this in statistical */ { "countif", "rS", N_("range,criteria"), &help_countif, gnumeric_countif, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "ceil", "f", N_("number"), &help_ceil, gnumeric_ceil, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "ceiling", "ff", N_("number,significance"), &help_ceiling, gnumeric_ceiling, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "degrees", "f", N_("number"), &help_degrees, gnumeric_degrees, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "even", "f", N_("number"), &help_even, gnumeric_even, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "exp", "f", N_("number"), &help_exp, gnumeric_exp, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "expm1", "f", N_("number"), &help_expm1, gnumeric_expm1, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "fact", "f", N_("number"), &help_fact, gnumeric_fact, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_SUPERSET, GNM_FUNC_TEST_STATUS_BASIC }, /* MS Excel puts this in the engineering functions */ { "factdouble", "f", N_("number"), &help_factdouble, gnumeric_factdouble, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "fib", "f", N_("number"), &help_fib, gnumeric_fib, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "combin", "ff", N_("n,k"), &help_combin, gnumeric_combin, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "floor", "f|f", N_("number"), &help_floor, gnumeric_floor, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "gcd", NULL, N_("number,number"), &help_gcd, NULL, gnumeric_gcd, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "hypot", NULL, "", &help_hypot, NULL, gnumeric_hypot, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "int", "f", N_("number"), &help_int, gnumeric_int, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "lcm", NULL, "", &help_lcm, NULL, gnumeric_lcm, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "ln", "f", N_("number"), &help_ln, gnumeric_ln, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "ln1p", "f", N_("number"), &help_ln1p, gnumeric_ln1p, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "log", "f|f", N_("number,base"), &help_log, gnumeric_log, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "log2", "f", N_("number"), &help_log2, gnumeric_log2, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "log10", "f", N_("number"), &help_log10, gnumeric_log10, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "mod", "ff", N_("numerator,denominator"), &help_mod, gnumeric_mod, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "mround", "ff", N_("number,multiple"), &help_mround, gnumeric_mround, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "multinomial", NULL, "", &help_multinomial, NULL, gnumeric_multinomial, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "odd" , "f", N_("number"), &help_odd, gnumeric_odd, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "power", "ff", N_("base,exponent"), &help_power, gnumeric_power, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "g_product", NULL, N_("number"), &help_g_product, NULL, gnumeric_g_product, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "quotient" , "ff", N_("numerator,denominator"), &help_quotient, gnumeric_quotient, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "radians", "f", N_("number"), &help_radians, gnumeric_radians, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "roman", "f|f", N_("number,type"), &help_roman, gnumeric_roman, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "round", "f|f", N_("number,digits"), &help_round, gnumeric_round, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "rounddown", "f|f", N_("number,digits"), &help_rounddown, gnumeric_rounddown, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "roundup", "f|f", N_("number,digits"), &help_roundup, gnumeric_roundup, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "seriessum", NULL, N_("x,n,m,coefficients"), &help_seriessum, NULL, gnumeric_seriessum, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sign", "f", N_("number"), &help_sign, gnumeric_sign, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sin", "f", N_("number"), &help_sin, gnumeric_sin, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "sinh", "f", N_("number"), &help_sinh, gnumeric_sinh, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "sqrt", "f", N_("number"), &help_sqrt, gnumeric_sqrt, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sqrtpi", "f", N_("number"), &help_sqrtpi, gnumeric_sqrtpi, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "suma", NULL, N_("number,number,"), &help_suma, NULL, gnumeric_suma, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumif", "rS|r", N_("range,criteria,actual_range"), &help_sumif, gnumeric_sumif, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumproduct", NULL, N_("range,range,"), &help_sumproduct, NULL, gnumeric_sumproduct, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumsq", NULL, N_("number"), &help_sumsq, NULL, gnumeric_sumsq, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumx2my2", "AA", N_("array1,array2"), &help_sumx2my2, gnumeric_sumx2my2, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumx2py2", "AA", N_("array1,array2"), &help_sumx2py2, gnumeric_sumx2py2, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumxmy2", "AA", N_("array1,array2"), &help_sumxmy2, gnumeric_sumxmy2, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "tan", "f", N_("number"), &help_tan, gnumeric_tan, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "tanh", "f", N_("number"), &help_tanh, gnumeric_tanh, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "trunc", "f|f", N_("number,digits"), &help_trunc, gnumeric_trunc, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "pi", "", "", &help_pi, gnumeric_pi, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, { "mmult", "AA", N_("array1,array2"), &help_mmult, gnumeric_mmult, NULL, NULL, NULL, NULL, GNM_FUNC_RETURNS_NON_SCALAR, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "minverse","A", N_("array"), &help_minverse, gnumeric_minverse, NULL, NULL, NULL, NULL, GNM_FUNC_RETURNS_NON_SCALAR, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "mdeterm", "A", N_("array,matrix_type,bandsize"), &help_mdeterm, gnumeric_mdeterm, NULL, NULL, NULL, NULL, GNM_FUNC_RETURNS_NON_SCALAR, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, #if 0 { "logmdeterm", "A|si", N_("array,matrix_type,bandsize"), &help_logmdeterm, gnumeric_logmdeterm, NULL, NULL, NULL }, #endif {NULL} };