/// <summary>
        /// Originally described at https://github.com/stevengj/nlopt/blob/master/src/api/optimize.c#L878
        /// </summary>
        internal static nlopt_result nlopt_optimize_limited(nlopt_opt opt, double[] x, int offset_x, ref double minf,
                                                            int maxeval, double maxtime)
        {
            int          save_maxeval;
            double       save_maxtime;
            nlopt_result ret;

            NLoptOptions.nlopt_unset_errmsg(opt);

            if (opt == null)
            {
                NLoptApi.RETURN_ERR(nlopt_result.NLOPT_INVALID_ARGS, opt, "NULL opt arg");
            }

            save_maxeval = NLoptApi.nlopt_get_maxeval(opt);
            save_maxtime = NLoptApi.nlopt_get_maxtime(opt);

            /* override opt limits if maxeval and/or maxtime are more stringent */
            if (save_maxeval <= 0 || (maxeval > 0 && maxeval < save_maxeval))
            {
                NLoptApi.nlopt_set_maxeval(opt, maxeval);
            }
            if (save_maxtime <= 0 || (maxtime > 0 && maxtime < save_maxtime))
            {
                NLoptApi.nlopt_set_maxtime(opt, maxtime);
            }

            ret = nlopt_optimize(opt, x, offset_x, ref minf);

            NLoptApi.nlopt_set_maxeval(opt, save_maxeval);
            NLoptApi.nlopt_set_maxtime(opt, save_maxtime);

            return(ret);
        }
        /// <summary>
        /// Originally described at https://github.com/stevengj/nlopt/blob/master/src/api/optimize.c#L813
        /// </summary>
        internal static nlopt_result nlopt_optimize(nlopt_opt opt, double[] x, int x_offset, ref double opt_f)
        {
            nlopt_func f;

            object[]      f_data;
            nlopt_precond pre;
            f_max_data    fmd = new f_max_data();
            bool          maximize;
            nlopt_result  ret;

            NLoptOptions.nlopt_unset_errmsg(opt);
            if (opt == null /*|| opt_f == null*/ || opt.f == null)
            {
                NLoptApi.RETURN_ERR(nlopt_result.NLOPT_INVALID_ARGS, opt, "NULL args to nlopt_optimize");
            }
            f      = opt.f;
            f_data = opt.f_data;
            pre    = opt.pre;

            // for maximizing, just minimize the f_max wrapper, which flips the sign of everything
            if ((maximize = opt.maximize))
            {
                fmd.f             = f;
                fmd.f_data        = f_data;
                fmd.pre           = pre;
                opt.f             = f_max;
                opt.f_data        = new object[] { fmd };
                opt.f_data_offset = 0;
                if (opt.pre != null)
                {
                    opt.pre = pre_max;
                }
                opt.stopval  = -opt.stopval;
                opt.maximize = false;
            }

            // possibly eliminate lb == ub dimensions for some algorithms
            {
                nlopt_opt elim_opt = opt;
                if (elimdim_wrapcheck(opt))
                {
                    elim_opt = elimdim_create(opt);
                    if (elim_opt == null)
                    {
                        NLoptOptions.nlopt_set_errmsg(opt, "failure allocating elim_opt");
                        ret = nlopt_result.NLOPT_OUT_OF_MEMORY;
                        goto done;
                    }
                    elimdim_shrink(opt.n, x, x_offset, opt.lb, opt.ub);
                }

                ret = nlopt_optimize_(elim_opt, x, x_offset, ref opt_f);

                if (elim_opt != opt)
                {
                    elimdim_destroy(elim_opt);
                    elimdim_expand(opt.n, x, x_offset, opt.lb, opt.ub);
                }
            }

done:
            if (maximize)  // restore original signs
            {
                opt.maximize = maximize;
                opt.stopval  = -opt.stopval;
                opt.f        = f;
                opt.f_data   = f_data;
                opt.pre      = pre;
                opt_f        = -opt_f;
            }

            return(ret);
        }