/* given opt, create a new opt with equal-constraint dimensions eliminated */
static nlopt_opt elimdim_create(nlopt_opt opt)
{
     nlopt_opt opt0 = nlopt_copy(opt);
     double *x, *grad = NULL;
     unsigned i;
     
     if (!opt0) return NULL;
     x = (double *) malloc(sizeof(double) * opt->n);
     if (opt->n && !x) { nlopt_destroy(opt0); return NULL; }

     if (opt->algorithm == NLOPT_GD_STOGO
         || opt->algorithm == NLOPT_GD_STOGO_RAND) {
	  grad = (double *) malloc(sizeof(double) * opt->n);
	  if (opt->n && !grad) goto bad;
     }

     opt0->n = elimdim_dimension(opt->n, opt->lb, opt->ub);
     elimdim_shrink(opt->n, opt0->lb, opt->lb, opt->ub);
     elimdim_shrink(opt->n, opt0->ub, opt->lb, opt->ub);
     elimdim_shrink(opt->n, opt0->xtol_abs, opt->lb, opt->ub);
     elimdim_shrink(opt->n, opt0->dx, opt->lb, opt->ub);

     opt0->munge_on_destroy = opt0->munge_on_copy = NULL;

     opt0->f = elimdim_func;
     opt0->f_data = elimdim_makedata(opt->f, NULL, opt->f_data,
				     opt->n, x, opt->lb, opt->ub, grad);
     if (!opt0->f_data) goto bad;

     for (i = 0; i < opt->m; ++i) {
	  opt0->fc[i].f = elimdim_func;
	  opt0->fc[i].mf = elimdim_mfunc;
	  opt0->fc[i].f_data = elimdim_makedata(opt->fc[i].f, opt->fc[i].mf,
						opt->fc[i].f_data,
						opt->n, x, opt->lb, opt->ub,
						NULL);
	  if (!opt0->fc[i].f_data) goto bad;
     }

     for (i = 0; i < opt->p; ++i) {
	  opt0->h[i].f = elimdim_func;
	  opt0->h[i].mf = elimdim_mfunc;
	  opt0->h[i].f_data = elimdim_makedata(opt->h[i].f, opt->h[i].mf,
					       opt->h[i].f_data,
					       opt->n, x, opt->lb, opt->ub,
					       NULL);
	  if (!opt0->h[i].f_data) goto bad;
     }

     return opt0;
bad:
     free(grad);
     free(x);
     nlopt_destroy(opt0);
     return NULL;
}
nlopt_result
NLOPT_STDCALL nlopt_set_local_optimizer(nlopt_opt opt,
					const nlopt_opt local_opt)
{
     if (opt) {
	  if (local_opt && local_opt->n != opt->n) return NLOPT_INVALID_ARGS;
	  nlopt_destroy(opt->local_opt);
	  opt->local_opt = nlopt_copy(local_opt);
	  if (local_opt) {
	       if (!opt->local_opt) return NLOPT_OUT_OF_MEMORY;
	       nlopt_set_lower_bounds(opt->local_opt, opt->lb);
	       nlopt_set_upper_bounds(opt->local_opt, opt->ub);
	       nlopt_remove_inequality_constraints(opt->local_opt);
	       nlopt_remove_equality_constraints(opt->local_opt);
	       nlopt_set_min_objective(opt->local_opt, NULL, NULL);
	       nlopt_set_munge(opt->local_opt, NULL, NULL);
	       opt->local_opt->force_stop = 0;
	  }
	  return NLOPT_SUCCESS;
     }
     return NLOPT_INVALID_ARGS;
}
nlopt_opt NLOPT_STDCALL nlopt_copy(const nlopt_opt opt)
{
     nlopt_opt nopt = NULL;
     unsigned i;
     if (opt) {
	  nlopt_munge munge;
	  nopt = (nlopt_opt) malloc(sizeof(struct nlopt_opt_s));
	  *nopt = *opt;
	  nopt->lb = nopt->ub = nopt->xtol_abs = NULL;
	  nopt->fc = nopt->h = NULL;
	  nopt->m_alloc = nopt->p_alloc = 0;
	  nopt->local_opt = NULL;
	  nopt->dx = NULL;
	  nopt->work = NULL;
	  opt->force_stop_child = NULL;

	  munge = nopt->munge_on_copy;
	  if (munge && nopt->f_data)
	       if (!(nopt->f_data = munge(nopt->f_data))) goto oom;

	  if (opt->n > 0) {
	       nopt->lb = (double *) malloc(sizeof(double) * (opt->n));
	       if (!opt->lb) goto oom;
	       nopt->ub = (double *) malloc(sizeof(double) * (opt->n));
	       if (!opt->ub) goto oom;
	       nopt->xtol_abs = (double *) malloc(sizeof(double) * (opt->n));
	       if (!opt->xtol_abs) goto oom;
	       
	       memcpy(nopt->lb, opt->lb, sizeof(double) * (opt->n));
	       memcpy(nopt->ub, opt->ub, sizeof(double) * (opt->n));
	       memcpy(nopt->xtol_abs, opt->xtol_abs, sizeof(double) * (opt->n));
	  }

	  if (opt->m) {
	       nopt->m_alloc = opt->m;
	       nopt->fc = (nlopt_constraint *) malloc(sizeof(nlopt_constraint)
						      * (opt->m));
	       if (!nopt->fc) goto oom;
	       memcpy(nopt->fc, opt->fc, sizeof(nlopt_constraint) * (opt->m));
	       for (i = 0; i < opt->m; ++i) nopt->fc[i].tol = NULL;
	       if (munge)
		    for (i = 0; i < opt->m; ++i)
			 if (nopt->fc[i].f_data &&
			     !(nopt->fc[i].f_data
			       = munge(nopt->fc[i].f_data)))
			      goto oom;
	       for (i = 0; i < opt->m; ++i)
		    if (opt->fc[i].tol) {
			 nopt->fc[i].tol = (double *) malloc(sizeof(double)
							     * nopt->fc[i].m);
			 if (!nopt->fc[i].tol) goto oom;
			 memcpy(nopt->fc[i].tol, opt->fc[i].tol,
				sizeof(double) * nopt->fc[i].m);
		    }
	  }

	  if (opt->p) {
	       nopt->p_alloc = opt->p;
	       nopt->h = (nlopt_constraint *) malloc(sizeof(nlopt_constraint)
						     * (opt->p));
	       if (!nopt->h) goto oom;
	       memcpy(nopt->h, opt->h, sizeof(nlopt_constraint) * (opt->p));
	       for (i = 0; i < opt->p; ++i) nopt->h[i].tol = NULL;
	       if (munge)
		    for (i = 0; i < opt->p; ++i)
			 if (nopt->h[i].f_data &&
			     !(nopt->h[i].f_data
			       = munge(nopt->h[i].f_data)))
			      goto oom;
	       for (i = 0; i < opt->p; ++i)
		    if (opt->h[i].tol) {
			 nopt->h[i].tol = (double *) malloc(sizeof(double)
							     * nopt->h[i].m);
			 if (!nopt->h[i].tol) goto oom;
			 memcpy(nopt->h[i].tol, opt->h[i].tol,
				sizeof(double) * nopt->h[i].m);
		    }
	  }

	  if (opt->local_opt) {
	       nopt->local_opt = nlopt_copy(opt->local_opt);
	       if (!nopt->local_opt) goto oom;
	  }

	  if (opt->dx) {
	       nopt->dx = (double *) malloc(sizeof(double) * (opt->n));
	       if (!nopt->dx) goto oom;
	       memcpy(nopt->dx, opt->dx, sizeof(double) * (opt->n));
	  }
     }
     return nopt;

oom:
     nopt->munge_on_destroy = NULL; // better to leak mem than crash
     nlopt_destroy(nopt);
     return NULL;
}