/*******************************************************************
Copyright (C) 2017, 2018 AMPL Optimization, Inc.; written by David M. Gay.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that the copyright notice and this permission notice and warranty
disclaimer appear in supporting documentation.

The author and AMPL Optimization, Inc. disclaim all warranties with
regard to this software, including all implied warranties of
merchantability and fitness.  In no event shall the author be liable
for any special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether in an
action of contract, negligence or other tortious action, arising out
of or in connection with the use or performance of this software.
*******************************************************************/

#include "jacpdim.h"

 static real
copeval(ps_func *f A_ASL)
{
	psb_elem *b, *be;
	expr *e;
	real t = 0.;

	for(b = f->b, be = b + f->nb; b < be; b++) {
		e = b->D.e;
		t += (*e->op)(e K_ASL);
		}
	return t;
	}

 static real
cogeval(ASL_pfgh *asl, ps_func *f)
{
	psb_elem *b, *be;
	psg_elem *g, *ge;
	expr *e;
	expr_v *V;
	linpart *L, *Le;
	real t, t1;

	V = var_e;
	t = 0.;
	for(g = f->g, ge = g + f->ng; g < ge; g++) {
		t1 = g->g0;
		if (g->nlin) {
			L = g->L;
			for(Le = L + g->nlin; L < Le; L++)
				t1 += L->fac * V[L->v.i].v;
			}
		if (g->ns) {
			b = g->E;
			for(be = b + g->ns; b < be; b++) {
				e = b->D.e;
				t1 += (*e->op)(e C_ASL);
				}
			}
		g->esum.v = t1;
		e = g->g;
		t += g->scale * (*e->op)(e C_ASL);
		}
	return t;
	}

 static void
psderprop(ASL_pfgh *asl, ps_func *f)
{
	int L;
	psb_elem *b, *be;
	psg_elem *g, *ge;
	expr *e, *ee;
	real *A, t, t1, t2;
	ograd *og;

	for(b = f->b, be = b + f->nb; b < be; b++)
		if ((L = b->D.zaplen)) {
			memset(adjoints_nv1, 0, L);
			derprop(b->D.d);
			}
	if (!f->ng)
		return;
	for(g = f->g, ge = g + f->ng; g < ge; g++) {
		e = g->g;
		ee = g->ge;
		if (e == ee) {
			t = e->dL;
			t2 = e->dL2;
			}
		else {
			t = e->dL;
			do {
				e = e->L.e;
				t *= e->dL;
				}
				while(e != ee);
			e = g->g;
			if (t != 0.) {
				t1 = ee->dL;
				t2 = ee->dL2 * (t / t1);
				for(;;) {
					ee = ee->R.e;
					t2 += (t / ee->dL) * t1 * ee->dL2;
					if (ee == e)
						break;
					t1 *= ee->dL;
					}
				}
			else
				for(L = 0, t2 = 1.;; e = e->L.e) {
					if (e->dL) {
						t2 *= e->dL;
						if (L)
							t2 *= e->dL;
						}
					else if (L++) {
						t2 = 0;
						break;
						}
					else
						t2 *= e->dL2;
					if (e == ee)
						break;
					}
			}
		g->g1 = t *= g->scale;
		g->g2 = t2 * g->scale;
		A = adjoints;
		for(og = g->og; og; og = og->next)
			A[og->varno] += t*og->coef;
		}
	}

 static void
psgcomp(ASL_pfgh *asl, ps_func *f)
{
	int n;
	ograd *og;
	psb_elem *b, *be;
	psg_elem *g, *ge;
	linpart *L, *Le;
	linarg *la, **lap, **lape, *tl;
	range *U;
	real t;
	real *A = adjoints;

	g = f->g;
	asl->P.npsgcomp++;
	for(ge = g + f->ng; g < ge; g++) {
		for(og = g->og; og; og = og->next)
			A[og->varno] = 0;
		if (g->nlin) {
			L = g->L;
			for(Le = L + g->nlin; L < Le; L++)
				A[L->v.i] = L->fac;
			}
		if (g->ns) {
			b = g->E;
			tl = 0;
			for(be = b + g->ns; b < be; b++) {
				if ((U = b->U) && (n = U->nintv)) {
					lap = U->lap;
					lape = lap + n;
					do {
						la = *lap++;
						if (!la->termno++) {
							A[la->v->a] = 0.;
							la->tnext = tl;
							tl = la;
							}
						}
						while(lap < lape);
					}
				}
			for(b = g->E; b < be; b++)
				if ((n = b->D.zaplen)) {
					memset(adjoints_nv1, 0, n);
					derprop(b->D.d);
					}
			while(tl) {
				tl->termno = 0;
				if ((t = A[tl->v->a])){
					og = tl->nz;
					do A[og->varno] += t*og->coef;
						while((og = og->next));
					}
				tl = tl->tnext;
				}
			}
		for(og = g->og; og; og = og->next)
			og->coef = A[og->varno];
		}
	}

 void
conpval_ASL(ASL *a, real *X, real *F, fint *nerror)
{
	ASL_pfgh *asl;
	Jmp_buf err_jmp0;
	cgrad *gr, **gr0;
	expr_n *en;
	int *cm, i, j, j1, je, kv, *ncxval, nx, *vmi;
	ps_func *p, *p0;
	real *cscale, f, *vscale;

	ASL_CHECK(a, ASL_read_pfgh, "conpval");
	asl = (ASL_pfgh*)a;
	if (nerror && *nerror >= 0) {
		err_jmp = &err_jmp0;
		i = setjmp(err_jmp0.jb);
		if ((*nerror = i))
			return;
		}
	want_deriv = want_derivs;
	errno = 0;	/* in case f77 set errno opening files */
	je = n_conjac[1];
	j = n_conjac[0];
	if (!asl->i.x_known) {
		co_index = j;
		xp_check_ASL(asl, X);
		}
	if (!(gr0 = asl->i.Cgrad0))
		asl->i.Cgrad0 = gr0 = asl->i.Cgrad_;
	p0 = asl->P.cps;
	cscale = asl->i.cscale;
	kv = 0;
	vmi = 0;
	if ((vscale = asl->i.vscale))
		kv = 2;
	if (asl->i.vmap) {
		vmi = get_vminv_ASL(a);
		++kv;
		}
	cm = asl->i.cmap;
	nx = a->i.nxval;
	ncxval = asl->i.ncxval;
	for(; j < je; j++) {
		i = j;
		if (cm)
			i = cm[i];
		p = p0 + i;
		if (p->nb) {
			co_index = i;
			f = copeval(p C_ASL);
			if (p->ng)
				f += cogeval(asl, p);
			}
		else if (p->ng) {
			co_index = i;
			f = cogeval(asl, p);
			}
		else {
			en = (expr_n*)con_de[i].e;
			f = en->v;
			}
		ncxval[i] = nx;
		if (!F)
			continue;
		gr = gr0[i];
		switch(kv) {
		  case 3:
			for(; gr; gr = gr->next) {
				j1 = vmi[gr->varno];
				f += X[j1] * vscale[j1] * gr->coef;
				}
			break;
		  case 2:
			for(; gr; gr = gr->next) {
				j1 = gr->varno;
				f += X[j1] * vscale[j1] * gr->coef;
				}
			break;
		  case 1:
			for(; gr; gr = gr->next)
				f += X[vmi[gr->varno]] * gr->coef;
			break;
		  case 0:
			for(; gr; gr = gr->next)
				f += X[gr->varno] * gr->coef;
		  }
		if (cscale)
			f *= cscale[j];
		*F++ = f;
		}
	err_jmp = 0;
	x0kind |= ASL_have_conval;
	}

 void
jacpval_ASL(ASL *a, real *X, real *G, fint *nerror)
{
	ASL_pfgh *asl;
	Jmp_buf err_jmp0;
	cgrad *gr, **gr0, *gr1;
	fint ne0;
	int *cm, i, i1, j, k, nx, *vmi, xksave;
	linarg *la, **lap, **lape;
	ograd *og;
	ps_func *p, *p0;
	psb_elem *b, *be;
	range *U;
	real *Adjoints, *cscale, t, *vscale;
	static char who[] = "jacpval";

	ASL_CHECK(a, ASL_read_pfgh, who);
	asl = (ASL_pfgh*)a;
	if (!want_derivs)
		No_derivs_ASL(who);
	ne0 = -1;
	if (nerror && (ne0 = *nerror) >= 0) {
		err_jmp = &err_jmp0;
		i = setjmp(err_jmp0.jb);
		if ((*nerror = i))
			return;
		}
	errno = 0;	/* in case f77 set errno opening files */
	co_index = j = n_conjac[0];
	if ((!asl->i.x_known && xp_check_ASL(asl,X))
	 || !(x0kind & ASL_have_conval)) {
		xksave = asl->i.x_known;
		conpval_ASL(a,X,0,nerror);
		asl->i.x_known = xksave;
		if (ne0 >= 0 && *nerror)
			return;
		}
	nx = asl->i.nxval;
	Adjoints = adjoints;
	gr0 = asl->i.Cgrad0;
	cscale = asl->i.cscale;
	vscale = asl->i.vscale;
	cm = asl->i.cmap;
	vmi = 0;
	if (asl->i.vmap)
		vmi = get_vminv_ASL(a);
	k = n_conjac[1];
	if (asl->i.Derrs)
		deriv_errchk_ASL(a, nerror, j, k-j);
	p0 = asl->P.cps;
	for(; j < k; ++j) {
		i = j;
		if (cm)
			i = cm[i];
		p = p0 + i;
		p->nxval = nx;
		if (p->ng)
			psgcomp(asl, p);
		for(gr = gr1 = gr0[i]; gr; gr = gr->next)
			Adjoints[gr->varno] = gr->coef;
		b = p->b;
		for(be = b + p->nb; b < be; b++)
			if ((U = b->U) && (i = U->nintv)) {
				lap = U->lap;
				lape = lap + i;
				do Adjoints[(*lap++)->v->a] = 0.;
					while(lap < lape);
				}
		b = p->b;
		psderprop(asl, p);
		while(b < be)
			if ((U = (b++)->U) && (i = U->nintv)) {
				lap = U->lap;
				lape = lap + i;
				while(lap < lape)
					if ((t = Adjoints[(la = *lap++)->v->a])){
						Adjoints[la->v->a] = 0;
						og = la->nz;
						do Adjoints[og->varno] +=
								t*og->coef;
							while((og = og->next));
						}
				}
		if (vscale) {
			gr = gr1;
			if (vmi)
				for(; gr; gr = gr->next) {
					i1 = gr->varno;
					Adjoints[i1] *= vscale[vmi[i1]];
					}
			else
				for(; gr; gr = gr->next) {
					i1 = gr->varno;
					Adjoints[i1] *= vscale[i1];
					}
			}
		gr = gr1;
		if (cscale)
			for(t = cscale[j]; gr; gr = gr->next)
				G[gr->goff] = t*Adjoints[gr->varno];
		else
			for(; gr; gr = gr->next)
				G[gr->goff] = Adjoints[gr->varno];
		}
	err_jmp = 0;
	}

 int
jacpdim_ASL(ASL *asl, const char *stub, fint *M, fint *N, fint *NO, fint *NZ,
	fint *MXROW, fint *MXCOL, ftnlen stub_len)
{
	FILE *nl;

	nl = jac_dim_ASL(asl, stub, M, N, NO, NZ, MXROW, MXCOL, stub_len);
	if (!nl)
		return ASL_readerr_nofile;
	X0 = (real *)M1alloc(n_var*sizeof(real));
	return pfgh_read_ASL(asl, nl, ASL_findgroups | ASL_return_read_err);
	}

/******** objpval, objpgrd ********/

 static void
NNOBJ_chk(ASL *asl, int i, const char *who)
{
	ASL_CHECK(asl, ASL_read_pfgh, who);
	if (i < 0 || i >= n_obj) {
		fprintf(Stderr,
			"%s: got NOBJ = %d; expected 0 <= NOBJ < %d\n",
			who, i, n_obj);
		exit(1);
		}
	}

 real
objpval_ASL(ASL *a, int i, real *X, fint *nerror)
{
	ASL_pfgh *asl;
	Jmp_buf err_jmp0;
	expr_n *en;
	int ij, j1, kv, *vmi;
	ograd *gr;
	ps_func *p;
	real f, *vscale;

	NNOBJ_chk(a, i, "objpval");
	asl = (ASL_pfgh*)a;
	if (nerror && *nerror >= 0) {
		err_jmp = &err_jmp0;
		ij = setjmp(err_jmp0.jb);
		if ((*nerror = ij))
			return 0.;
		}
	want_deriv = want_derivs;
	errno = 0;	/* in case f77 set errno opening files */
	co_index = -(i + 1);
	if (!asl->i.x_known)
		xp_check_ASL(asl,X);
	p = asl->P.ops + i;
	if (p->nb) {
		f = copeval(p C_ASL);
		if (p->ng)
			f += cogeval(asl, p);
		}
	else if (p->ng)
		f = cogeval(asl, p);
	else {
		en = (expr_n*)obj_de[i].e;
		f = en->v;
		}
	asl->i.noxval[i] = asl->i.nxval;
	gr = Ograd[i];
	kv = 0;
	vmi = 0;
	if ((vscale = asl->i.vscale))
		kv = 2;
	if (asl->i.vmap) {
		vmi = get_vminv_ASL(a);
		++kv;
		}
	switch(kv) {
	  case 3:
		for(; gr; gr = gr->next) {
			j1 = vmi[gr->varno];
			f += X[j1] * vscale[j1] * gr->coef;
			}
		break;
	  case 2:
		for(; gr; gr = gr->next) {
			j1 = gr->varno;
			f += X[j1] * vscale[j1] * gr->coef;
			}
		break;
	  case 1:
		for(; gr; gr = gr->next)
			f += X[vmi[gr->varno]] * gr->coef;
		break;
	  case 0:
		for(; gr; gr = gr->next)
			f += X[gr->varno] * gr->coef;
	  }
	err_jmp = 0;
	return f;
	}

 void
objpgrd_ASL(ASL *a, int i, real *X, real *G, fint *nerror)
{
	ASL_pfgh *asl;
	Jmp_buf err_jmp0;
	fint ne0;
	int ij, j, *vmi, xksave, *z;
	linarg *la;
	ograd *gr, *gr0;
	ps_func *p;
	real *Adjoints;
	real t, *vscale;
	static char who[] = "objpgrd";

	NNOBJ_chk(a, i, who);
	asl = (ASL_pfgh*)a;
	if (!want_derivs)
		No_derivs_ASL(who);
	p = asl->P.ops + i;
	ne0 = -1;
	if (nerror && (ne0 = *nerror) >= 0) {
		err_jmp = &err_jmp0;
		ij = setjmp(err_jmp0.jb);
		if ((*nerror = ij))
			return;
		}
	errno = 0;	/* in case f77 set errno opening files */
	co_index = -(i + 1);
	if (!asl->i.x_known)
		xp_check_ASL(asl,X);
	if (!asl->i.noxval || asl->i.noxval[i] != asl->i.nxval) {
		xksave = asl->i.x_known;
		asl->i.x_known = 1;
		objpval_ASL(a, i, X, nerror);
		asl->i.x_known = xksave;
		if (ne0 >= 0 && *nerror)
			return;
		}
	if (asl->i.Derrs)
		deriv_errchk_ASL(a, nerror, -(i+1), 1);
	Adjoints = adjoints;
	p->nxval = asl->i.nxval;
	if (p->ng)
		psgcomp(asl, p);
	gr0 = Ograd[i];
	for(gr = gr0; gr; gr = gr->next)
		Adjoints[gr->varno] = gr->coef;
	for(la = asl->P.lalist; la; la = la->lnext)
		Adjoints[la->v->a] = 0.;
	psderprop(asl, p);
	if (!G)
		return;
	for(la = asl->P.lalist; la; la = la->lnext)
		if ((t = Adjoints[la->v->a])) {
			gr = la->nz;
			do Adjoints[gr->varno] += t*gr->coef;
				while((gr = gr->next));
			}
	if (zerograds) {	/* sparse gradients */
		z = zerograds[i];
		while((j = *z++) >= 0)
			G[j] = 0;
		}
	gr = gr0;
	vmi = 0;
	if (asl->i.vmap)
		vmi = get_vminv_ASL(a);
	if ((vscale = asl->i.vscale)) {
		if (vmi)
			for(; gr; gr = gr->next) {
				j = vmi[i = gr->varno];
				G[j] = Adjoints[i] * vscale[j];
				}
		else
			for(; gr; gr = gr->next) {
				i = gr->varno;
				G[i] = Adjoints[i] * vscale[i];
				}
		}
	else if (vmi)
		for(; gr; gr = gr->next) {
			i = gr->varno;
			G[vmi[i]] = Adjoints[i];
			}
	else
		for(; gr; gr = gr->next) {
			i = gr->varno;
			G[i] = Adjoints[i];
			}
	err_jmp = 0;
	}

 static void
INchk(ASL *asl, const char *who, int i, int ix)
{
	ASL_CHECK(asl, ASL_read_pfgh, who);
	if (i < 0 || i >= ix) {
		fprintf(Stderr, "%s: got I = %d; expected 0 <= I < %d\n",
			who, i, ix);
		exit(1);
		}
	}

 static real
cpval(ASL_pfgh *asl, int i, real *X, fint *nerror)
{
	Jmp_buf err_jmp0;
	expr *e;
	expr_n *en;
	int L, nc;
	ps_func *p;
	real f;

	if (nerror && *nerror >= 0) {
		err_jmp = &err_jmp0;
		L = setjmp(err_jmp0.jb);
		if ((*nerror = L))
			return 0.;
		}
	want_deriv = want_derivs;
	errno = 0;	/* in case f77 set errno opening files */
	co_index = i;
	if (!asl->i.x_known)
		xp_check_ASL(asl, X);
	if (i >= (nc = asl->i.n_con0)) {
		e = lcon_de[i-nc].e;
		f = (*e->op)(e C_ASL);
		goto done;
		}
	p = asl->P.cps + i;
	if (p->nb) {
		f = copeval(p C_ASL);
		if (p->ng)
			f += cogeval(asl, p);
		}
	else if (p->ng)
		f = cogeval(asl, p);
	else {
		en = (expr_n*)con_de[i].e;
		f = en->v;
		}
 done:
	asl->i.ncxval[i] = asl->i.nxval;
	err_jmp = 0;
	return f;
	}

 static real
Conivalp(ASL_pfgh *asl, int i, real *X, fint *nerror)
{
	cgrad *gr;
	int j1, kv, *vmi;
	real f, *vscale;

	if (i < asl->i.n_con0)
		f = cpval(asl, i, X, nerror);
	else
		f = 0.;
	kv = 0;
	vmi = 0;
	if ((vscale = asl->i.vscale))
		kv = 2;
	if (asl->i.vmap) {
		vmi = get_vminv_ASL((ASL*)asl);
		++kv;
		}
	gr = asl->i.Cgrad0[i];
	switch(kv) {
	  case 3:
		for(; gr; gr = gr->next) {
			j1 = vmi[gr->varno];
			f += X[j1] * vscale[j1] * gr->coef;
			}
		break;
	  case 2:
		for(; gr; gr = gr->next) {
			j1 = gr->varno;
			f += X[j1] * vscale[j1] * gr->coef;
			}
		break;
	  case 1:
		for(; gr; gr = gr->next)
			f += X[vmi[gr->varno]] * gr->coef;
		break;
	  case 0:
		for(; gr; gr = gr->next)
			f += X[gr->varno] * gr->coef;
	  }
	return f;
	}

 real
conpival_nomap_ASL(ASL *a, int i, real *X, fint *nerror)
{
	INchk(a, "conpival_nomap", i, a->i.n_con0);
	return  Conivalp((ASL_pfgh*)a, i, X, nerror);
	}

 real
conpival_ASL(ASL *a, int i, real *X, fint *nerror)
{
	int *cm;

	INchk(a, "conpival", i, a->i.n_con_);
	if ((cm = a->i.cmap))
		i = cm[i];
	return  Conivalp((ASL_pfgh*)a, i, X, nerror);
	}

 int
lconpval(ASL *a, int i, real *X, fint *nerror)
{
	real f;

	INchk(a, "lconpival", i, a->i.n_lcon_);
	f = cpval((ASL_pfgh*)a, i + a->i.n_con0, X, nerror);
	return f != 0.;
	}

 static void
Congrdp(ASL_pfgh *asl, int i, real *X, real *G, fint *nerror)
{
	Jmp_buf err_jmp0;
	cgrad *gr, *gr0;
	fint ne0;
	int i0, j, *vmi, xksave;
	linarg *la, **lap, **lape;
	ograd *og;
	ps_func *p;
	psb_elem *b, *be;
	range *U;
	real *Adjoints, t, *vscale;

	ne0 = -1;
	if (nerror && (ne0 = *nerror) >= 0) {
		err_jmp = &err_jmp0;
		i0 = setjmp(err_jmp0.jb);
		if ((*nerror = i0))
			return;
		}
	errno = 0;	/* in case f77 set errno opening files */
	if (!asl->i.x_known) {
		co_index = i;
		xp_check_ASL(asl, X);
		}
	if ((!asl->i.ncxval || asl->i.ncxval[i] != asl->i.nxval)
	 && (!(x0kind & ASL_have_conval)
	     || i < n_conjac[0] || i >= n_conjac[1])) {
		xksave = asl->i.x_known;
		asl->i.x_known = 1;
		conpival_ASL((ASL*)asl,i,X,nerror);
		asl->i.x_known = xksave;
		if (ne0 >= 0 && *nerror)
			return;
		}
	if (asl->i.Derrs)
		deriv_errchk_ASL((ASL*)asl, nerror, i, 1);
	Adjoints = adjoints;
	p = asl->P.cps + i;
	p->nxval = asl->i.nxval;
	gr0 = asl->i.Cgrad0[i];
	if (p->ng)
		psgcomp(asl, p);
	for(gr = gr0; gr; gr = gr->next)
		Adjoints[gr->varno] = gr->coef;
	b = p->b;
	for(be = b + p->nb; b < be; b++)
		if ((U = b->U) && (i = U->nintv)) {
			lap = U->lap;
			lape = lap + i;
			do Adjoints[(*lap++)->v->a] = 0.;
				while(lap < lape);
			}
	b = p->b;
	psderprop(asl, p);
	if (!G)
		return;
	while(b < be)
		if ((U = (b++)->U) && (i = U->nintv)) {
			lap = U->lap;
			lape = lap + i;
			while(lap < lape)
				if ((t = Adjoints[(la = *lap++)->v->a])){
					Adjoints[la->v->a] = 0;
					og = la->nz;
					do Adjoints[og->varno] +=
							t*og->coef;
						while((og = og->next));
					}
			}

	vmi = 0;
	if (asl->i.vmap)
		vmi = get_vminv_ASL((ASL*)asl);
	if ((vscale = asl->i.vscale)) {
		gr = gr0;
		if (vmi)
			for(; gr; gr = gr->next) {
				i0 = gr->varno;
				Adjoints[i0] *= vscale[vmi[i0]];
				}
		else
			for(; gr; gr = gr->next) {
				i0 = gr->varno;
				Adjoints[i0] *= vscale[i0];
				}
		}
	gr = gr0;
	i0 = 0;
	switch(asl->i.congrd_mode) {
	  case 1:
		for(; gr; gr = gr->next)
			G[i0++] = Adjoints[gr->varno];
		break;
	  case 2:
		for(; gr; gr = gr->next)
			G[gr->goff] = Adjoints[gr->varno];
		break;
	  default:
		if (vmi) {
			for(; gr; gr = gr->next) {
				i = vmi[j = gr->varno];
				while(i0 < i)
					G[i0++] = 0;
				G[i] = Adjoints[j];
				i0 = i + 1;
				}
			}
		else
			for(; gr; gr = gr->next) {
				i = gr->varno;
				while(i0 < i)
					G[i0++] = 0;
				G[i] = Adjoints[i];
				i0 = i + 1;
				}
		i = n_var;
		while(i0 < i)
			G[i0++] = 0;
	  }
	err_jmp = 0;
	}

 void
conpgrd_nomap_ASL(ASL *a, int i, real *X, real *G, fint *nerror)
{
	ASL_pfgh *asl;
	static char who[] = "conpgrd_nomap";

	INchk(a, who, i, a->i.n_con0);
	asl = (ASL_pfgh*)a;
	if (!want_derivs)
		No_derivs_ASL(who);
	Congrdp(asl, i, X, G, nerror);
	}

 void
conpgrd_ASL(ASL *a, int i, real *X, real *G, fint *nerror)
{
	ASL_pfgh *asl;
	int *cm;
	static char who[] = "conpgrd";

	INchk(a, who, i, a->i.n_con_);
	asl = (ASL_pfgh*)a;
	if (!want_derivs)
		No_derivs_ASL(who);
	if ((cm = asl->i.cmap))
		i = cm[i];
	Congrdp(asl, i, X, G, nerror);
	}

 static void
xpsgchk(ASL_pfgh *asl, ps_func *f0, int *xv, int n, int nx,
	real (*ev)(ASL *a, int i, real *X, fint *nerror),
	void (*gv)(ASL *a, int i, real *X, real *G, fint *nerror),
	real *y, int oxk, int isobj)
{
	int i, i1, i2;
	ps_func *f;

	i1 = i2 = -1;
	for(i = 0; i < n; i++)
		if (y[i] != 0.) {
			if (i1 < 0)
				i1 = i;
			i2 = i;
			if (xv[i] != nx)
				(*ev)((ASL*)asl, i, Lastx, 0);
			f = f0 + i;
			if (f->ng && f->nxval != nx)
				(*gv)((ASL*)asl, i, Lastx, 0, 0);
			}
	if (i1 >= 0 && asl->i.Derrs) {
		asl->i.x_known = oxk;
		for(i = i1; i <= i2; i = i1) {
			i1 = i + 1;
			if (y[i]) {
				while(i1 <= i2 && y[i1])
					++i1;
				deriv_errchk_ASL((ASL*)asl, 0, isobj ? -(i+1) : i, i1-i);
				}
			}
		asl->i.x_known = 1;
		}
	}

 void
xpsg_check_ASL(ASL_pfgh *asl, int nobj, real *ow, real *y)
{
	int i, nc, no, nx, nz, oxk, tno, *xv;
	ps_func *f;
	real t, *x;

	nc = nlc;
	no = nlo;
	if (x0kind == ASL_first_x) {
		if (!(x = X0))
			memset(x = Lastx, 0, n_var*sizeof(real));
		if (y) {
			for(i = 0; i < nc; ++i) {
				if (y[i]) {
					co_index = i;
					goto chk;
					}
				}
			}
		if (ow) {
			for(i = 0; i < no; ++i) {
				if (ow[i]) {
					co_index = -(i+1);
					goto chk;
					}
				}
			}
		if (nobj >= 0 && nobj < no)
			co_index = -(nobj + 1);
		else if (no)
			co_index = -1;
		else
			co_index = 0;
 chk:
		xp_check_ASL(asl, x);
		}
	tno = -1;
	if (!no) {
		if (!nc)
			return;
		ow = 0;
		}
	else if (ow) {
		for(i = 0; i < no; ++i) {
			if ((t = ow[i])) {
				if (t != 1. || tno >= 0) {
					tno = -2;
					break;
					}
				tno = i;
				}
			}
		}
	else if (nobj >= 0 && nobj < no)
		tno = nobj;
	if (!(x = asl->P.oyow))
		asl->P.oyow = x = (real*)M1alloc((nc + no)*sizeof(real));
	else {
		if (asl->P.onxval != asl->i.nxval
		 || asl->P.onobj != tno)
			goto work;
		if (tno == -2 && memcmp(ow, x, no*sizeof(real)))
			goto work;
		if (nc) {
			if (y) {
				if (memcmp(y, x+no, nc*sizeof(real)))
					goto work;
				}
			else if (asl->P.nynz)
				goto work;
			}
		return;
		}
 work:
	if (asl->P.ihdcur)
		ihd_clear_ASL(asl);
	asl->P.onxval = nx = asl->i.nxval;
	asl->P.onobj = tno;
	if (no) {
		if (ow)
			memcpy(x, ow, no*sizeof(real));
		else
			memset(x, 0, no*sizeof(real));
		x += no;
		}
	nz = 0;
	if (nc) {
		if (y) {
			for(i = 0; i < nc; ++i)
				if ((x[i] = y[i]))
					++nz;
			}
		else
			memset(x, 0, nc*sizeof(real));
		}
	else
		y = 0;
	asl->P.nynz = nz;
	oxk = asl->i.x_known;
	asl->i.x_known = 1;
	if (y)
		xpsgchk(asl, asl->P.cps, asl->i.ncxval, nc,
			nx, conpival_ASL, conpgrd_ASL, y, oxk, 0);
	f = asl->P.ops;
	xv = asl->i.noxval;
	if (nobj >= 0 && nobj < n_obj) {
		if (nobj >= no)
			goto done;
		if (ow) {
			ow += nobj;
			if (*ow == 0.)
				goto done;
			}
		if (xv[nobj] != nx)
			objpval_ASL((ASL*)asl, nobj, Lastx, 0);
		f += nobj;
		if (!f->ng)
			goto done;
		if (f->nxval != nx)
			objpgrd_ASL((ASL*)asl, nobj, Lastx, 0, 0);
		}
	else if (ow && no)
		xpsgchk(asl, f, xv, no,
			nx, objpval_ASL, objpgrd_ASL, ow, oxk, 1);
 done:
	asl->i.x_known = oxk;
	return;
	}
