/*
 * The Spar Library - modular math parser
 * Copyright (C) 2000,2001 Davide Angelocola <davide178@inwind.it>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 */

#include <string.h>

#include <spar/sl_parser.h>
#include <spar/sl_math_library.h>
#include <spar/sl_io.h>
#include <spar/sl_sort.h>
#include <spar/sl_table.h>

sl_function sl_function_TABLE[] = {
  /* one arg functions... */
  {"sin", "sin",		/* name to invoke this function */
   SL_FUNCTION_WITH_ONE_ARG,	/* how many arguments it gets   */
   SL_FUNCTION_CLASS_TRIG,	/* math type (for help command) */
   sl_sin,			/* function to call (one arg)   */
   (sl_function_tp) NULL	/* function to call (two args)  */
   }
  ,

  {"cos", "cos",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_TRIG,
   sl_cos,
   (sl_function_tp) NULL}
  ,

  {"tan", "tan",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_TRIG,
   sl_tan,
   (sl_function_tp) NULL}
  ,

  {"cotan", "cos(x) / sin(x)",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_TRIG,
   sl_cotan,
   (sl_function_tp) NULL}
  ,


  {"asin", "arcsin",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_ITRIG,
   sl_arcsin,
   (sl_function_tp) NULL}
  ,

  {"acos", "arccos",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_ITRIG,
   sl_arccos,
   (sl_function_tp) NULL}
  ,

  {"atan", "arctan",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_ITRIG,
   sl_arctan,
   (sl_function_tp) NULL}
  ,

  {"sinh", "sinh",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_HYPER,
   sl_sinh,
   (sl_function_tp) NULL}
  ,

  {"cosh", "cosh",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_HYPER,
   sl_cosh,
   (sl_function_tp) NULL}
  ,

  {"tanh", "tanh",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_HYPER,
   sl_tanh,
   (sl_function_tp) NULL}
  ,

  {"asinh", "arcsinh",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_HYPER,
   sl_asinh,
   (sl_function_tp) NULL}
  ,

  {"acosh", "arccosh",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_HYPER,
   sl_acosh,
   (sl_function_tp) NULL}
  ,

  {"atanh", "arctanh",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_HYPER,
   sl_atanh,
   (sl_function_tp) NULL}
  ,

  {"exp", "exp",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_EXP,
   sl_exp,
   (sl_function_tp) NULL}
  ,

  {"expm1", "expm1",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_EXP,
   sl_expm1,
   (sl_function_tp) NULL}
  ,

  {"log", "log",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_LOG,
   sl_log,
   (sl_function_tp) NULL}
  ,

  {"log2", "log2",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_LOG,
   sl_log2,
   (sl_function_tp) NULL}
  ,

  {"log10", "log10",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_LOG,
   sl_log10,
   (sl_function_tp) NULL}
  ,

  {"log1p", "log1p",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_LOG,
   sl_log1p,
   (sl_function_tp) NULL}
  ,


  {"floor", "floor",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_floor,
   (sl_function_tp) NULL}
  ,

  {"ceil", "ceil",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_ceil,
   (sl_function_tp) NULL}
  ,

  {"sgn", "sgn",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_sgn,
   (sl_function_tp) NULL}
  ,

  {"abs", "abs",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_abs,
   (sl_function_tp) NULL}
  ,

  {"deg", "deg",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_deg,
   (sl_function_tp) NULL}
  ,

  {"rad", "rad",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_rad,
   (sl_function_tp) NULL}
  ,

  {"fat", "!",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_fat,
   (sl_function_tp) NULL}
  ,

  {"sum", "S",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_sum,
   (sl_function_tp) NULL}
  ,

  {"inv", "inv",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_inv,
   (sl_function_tp) NULL}
  ,

  {"max", "M",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   (sl_function_p) NULL,
   sl_max}
  ,

  {"min", "m",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   (sl_function_p) NULL,
   sl_min}
  ,

  {"sqrn", "x^(1/y)",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_EXP,
   (sl_function_p) NULL,
   sl_sqrn}
  ,

  {"hypot", "hypot",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_ITRIG,
   (sl_function_p) NULL,
   sl_hypot}
  ,

  {"mcd", "mcd",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   (sl_function_p) NULL,
   sl_mcd}
  ,

  {"atan2", "arctan(y/x)",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_ITRIG,
   (sl_function_p) NULL,
   sl_atan2}
  ,


  {"expn", "expn",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_EXP,
   (sl_function_p) NULL,
   sl_expn}
  ,
/*
 *  Function 'pow' and 'mod' removed
 *  replaced with the parser operators:
 *  	'^'	for pow
 *  	'%' 	for mod
 *  	
 *  uncomment if you want to call this operator also as function
 */

/*
  {"pow", "pow",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_EXP,
   (sl_function_p) NULL,
   sl_pow}
  ,

  {"mod", "rem",
   SL_FUNCTION_WITH_TWO_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   (sl_function_p) NULL,
   sl_mod}
  ,
*/
  {"isnan", "isnan",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_isnan,
   (sl_function_tp) NULL}
  ,

  {"isinf", "isinf",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_isinf,
   (sl_function_tp) NULL}
  ,

  {"isfinite", "finite",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_finite,
   (sl_function_tp) NULL}
  ,

  {"fib", "fibonacci",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_SPECIAL,
   sl_fib,
   (sl_function_tp) NULL}
  ,

  {"pow2", "2^x",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_EXP,
   sl_pow2,
   (sl_function_tp) NULL}
  ,


  {"sqrt", "square root",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_EXP,
   sl_sqrt,
   (sl_function_tp) NULL}
  ,

  {"cbrt", "cube root",
   SL_FUNCTION_WITH_ONE_ARG,
   SL_FUNCTION_CLASS_EXP,
   sl_cbrt,
   (sl_function_tp) NULL}
  ,
};

const char *sl_function_type_TABLE[SL_FUNCTION_CLASS_COUNTER] = {
  "Trigonometric",
  "Inverse Trigonometric",
  "Exponential",
  "Logarithm",
  "Hyperbolic",
  "Special",
  "User_defined"
};

const char *sl_function_args_TABLE[SL_FUNCTION_WITH_COUNTER] = {
  "()",
  "(x)",
  "(x,y)",
  "(...)"
};

const int SL_FUNCTION_TABLE_LENGTH =
  sizeof (sl_function_TABLE) / sizeof (sl_function_TABLE[0]);

int
__function_cmp (const sl_function * f1, const sl_function * f2)
{
  return strncmp (f1->name, f2->name, SL_IDENTIFIER_LENGHT);
}

int
sl_function_init (void)
{
  return sl_qsort (sl_function_TABLE,
		   SL_FUNCTION_TABLE_LENGTH,
		   sizeof (sl_function),
		   (int (*)(const void *, const void *)) __function_cmp);
}

int
sl_function_check (const char *name)
{
  sl_function *r;

  r =
    sl_bsearch (name, sl_function_TABLE, SL_FUNCTION_TABLE_LENGTH,
		sizeof (sl_function),
		(int (*)(const void *, const void *)) __function_cmp);

  if (r)
    return ((int) r - (int) sl_function_TABLE) / sizeof (sl_function);
  else
    return SL_SYMBOL_NOT_FOUND;
}

void
sl_function_print_all (void)
{
  sl_table_column function_cols[] = {
    {"Name", SL_IDENTIFIER_LENGHT},
    {"Arg", 4},
    {"Math Form", SL_MESSAGE_LENGHT},
    {{0}}
  };

  sl_table_create ("Functions",
		   function_cols,
		   SL_FUNCTION_TABLE_LENGTH, sl_table_print_function);
}

int
sl_function_make_help (const char *name, sl_function_help * tmp)
{
  int fnc_code = sl_function_check (name);

  if (fnc_code != SL_SYMBOL_NOT_FOUND)
    {
      strcat (strcpy (tmp->math, sl_function_TABLE[fnc_code].math),
	      sl_function_args_TABLE[sl_function_TABLE[fnc_code].args]);
      strcat (strcpy
	      (tmp->type,
	       sl_function_type_TABLE[sl_function_TABLE[fnc_code].type]),
	      " function");
      strcpy (tmp->name, sl_function_TABLE[fnc_code].name);
      tmp->args = sl_function_TABLE[fnc_code].args;
      return 1;
    }
  return 0;
}

double
sl_function_run (const char *name, double argv[])
{
  sl_function *r;

  r =
    sl_bsearch ((const void *) name, sl_function_TABLE,
		SL_FUNCTION_TABLE_LENGTH, sizeof (sl_function),
		(int (*)(const void *, const void *)) __function_cmp);

  switch (r->args)
    {
    case SL_FUNCTION_WITH_ONE_ARG:
      return r->func (argv[0]);
      break;

    case SL_FUNCTION_WITH_TWO_ARG:
      return r->func2 (argv[0], argv[1]);
      break;

    case SL_FUNCTION_WITH_VAR_ARG:
    case SL_FUNCTION_WITH_COUNTER:
      break;
    }

  return 0.0;
}

sl_function_args_t
sl_function_get_argc (int code)
{
  return sl_function_TABLE[code].args;
}

const char *
sl_function_get_name (int code)
{
  return sl_function_TABLE[code].name;
}


syntax highlighted by Code2HTML, v. 0.9.1