

// PKG_ADD: autoload ("GSL_OCTAVE_NAME", which ("gsl_sf"));
DEFUN_DLD(GSL_OCTAVE_NAME, args, nargout, "\
  -*- texinfo -*-\n\
@deftypefn {Loadable Function} {@var{z} =} GSL_OCTAVE_NAME (@var{arg1}, @var{arg2}, @var{arg3}, @var{arg4}, @var{mode})\n\
@deftypefnx {Loadable Function} {[@var{z}, @var{err}] =} GSL_OCTAVE_NAME (@dots{})\n\
\n\
GSL_FUNC_DOCSTRING
\n\
@var{err} contains an estimate of the absolute error in the value @var{z}.\n\
\n\
The argument @var{mode} must be an integer corresponding to\n\
\n\
@table @asis\n\
@item 0 = GSL_PREC_DOUBLE\n\
    Double-precision, a relative accuracy of approximately @code{2e-16}.\n\
@item 1 = GSL_PREC_SINGLE\n\
    Single-precision, a relative accuracy of approximately @code{1e-7}.\n\
@item 2 = GSL_PREC_APPROX\n\
    Approximate values, a relative accuracy of approximately @code{5e-4}.\n\
@end table\n\
\n\
This function is from the GNU Scientific Library,\n\
see @url{http://www.gnu.org/software/gsl/} for documentation.\n\
DEPRECATION_WARNING
@end deftypefn\n")
{
#ifdef HAVE_GSL_FUNC

  gsl_set_error_handler (octave_gsl_errorhandler);

  // Expected number of input arguments
  const int nb_args = 5;

  // Check the actual number of input arguments
  if (args.length() != nb_args)
    {
      print_usage ();
      return octave_value ();
    }

  // Check that all input arguments are real
  for (int i = 0; i < nb_args; i++)
    if (! ISREAL(args(i)))
      {
        error ("Input argument #%d has a complex value. "
               "A real value was expected.", i + 1);
        print_usage ();
        return octave_value ();
      }

  // Check that the mode argument is scalar
  if (! args(4).is_scalar_type ())
    {
      error ("The mode argument must be scalar.");
      print_usage ();
      return octave_value ();
    }

  // Check that the mode argument has a valid value
  double tmp_mode_dbl = args(4).double_value ();
  gsl_mode_t mode;
  if (tmp_mode_dbl == 0.0)
    mode = GSL_PREC_DOUBLE;
  else if (tmp_mode_dbl == 1.0)
    mode = GSL_PREC_SINGLE;
  else if (tmp_mode_dbl == 2.0)
    mode = GSL_PREC_APPROX;
  else
    {
      error ("The 'mode' argument must be 0, 1, or 2.");
      print_usage ();
      return octave_value ();
    }

  // Get input arguments as NDArray objects
  NDArray arg1 = args(0).array_value();
  NDArray arg2 = args(1).array_value();
  NDArray arg3 = args(2).array_value();
  NDArray arg4 = args(3).array_value();

  // Get the dimensions of the arrays
  dim_vector dim (1, 1);
  octave_idx_type numel = 1;
  bool conformant = true;
  bool arg1_scalar = check_arg_dim<NDArray> (arg1, dim, numel, conformant);
  bool arg2_scalar = check_arg_dim<NDArray> (arg2, dim, numel, conformant);
  bool arg3_scalar = check_arg_dim<NDArray> (arg3, dim, numel, conformant);
  bool arg4_scalar = check_arg_dim<NDArray> (arg4, dim, numel, conformant);

  // Error in case of non-conformant arguments
  if (! conformant)
    {
      error ("Non-scalar input arguments must all have the same size.");
      return octave_value ();
    }

  // Create one separate index for each argument
  octave_idx_type i1 = 0;
  octave_idx_type i2 = 0;
  octave_idx_type i3 = 0;
  octave_idx_type i4 = 0;

  // Create one separate increment for each argument
  octave_idx_type inc1 = (arg1_scalar ? 0 : 1);
  octave_idx_type inc2 = (arg2_scalar ? 0 : 1);
  octave_idx_type inc3 = (arg3_scalar ? 0 : 1);
  octave_idx_type inc4 = (arg4_scalar ? 0 : 1);

  // Get pointers to the data of the input arrays
  const double *arg1_data = arg1.data ();
  const double *arg2_data = arg2.data ();
  const double *arg3_data = arg3.data ();
  const double *arg4_data = arg4.data ();

  // Array of output values
  NDArray y (dim);

  // GSL structure that will hold each individual result
  gsl_sf_result result;

  if (nargout < 2)
    {
      // Run the calculation
      for (octave_idx_type i = 0; i < numel; i++, i1 += inc1, i2 += inc2, i3 += inc3, i4 += inc4)
        {
          GSL_FUNC_NAME (arg1_data[i1], arg2_data[i2], arg3_data[i3], arg4_data[i4], mode, &result);
          y.xelem(i) = result.val;
        }

      return octave_value (y);
    }
  else
    {
      // Array of error estimates
      NDArray err (dim);

      // Run the calculation
      for (octave_idx_type i = 0; i < numel; i++, i1 += inc1, i2 += inc2, i3 += inc3, i4 += inc4)
        {
          GSL_FUNC_NAME (arg1_data[i1], arg2_data[i2], arg3_data[i3], arg4_data[i4], mode, &result);
          y.xelem(i) = result.val;
          err.xelem(i) = result.err;
        }

      octave_value_list retval (2);
      retval(0) = octave_value (y);
      retval(1) = octave_value (err);
      return retval;
    }

#else // HAVE_GSL_FUNC undefined

  error ("GSL function GSL_FUNC_NAME was found missing when "
         "the gsl package for octave was built.");
  return octave_value ();

#endif // HAVE_GSL_FUNC
}
