//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//
//
// $Log: nbh-ops.desc,v $
// Revision 1.3  1994/05/08  19:42:50  thoth
// Automatic neighborhood reductions require change in spec file.
//
// Revision 1.2  1994/04/15  13:19:55  thoth
// removed neighborhood_ prefix from reductions.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//
//
// Things that frob Bit images
//
//"C#define NEED_SPARE
// Not really, one of the other reductions below defines the spare for us.
// neighborhood int sum IA_Bit zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_IA_Bit
#define zero_extend_scan_IA_Bit

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const IA_Bit **src,
				 IA_Bit **dest,
				 IA_Bit zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,IA_Bit> *srciter,
				  IA_Bit **dest,
				  IA_Bit zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    IA_Bit	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    IA_Bit	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,IA_Bit> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, IA_Bit *dest_data,
			IA_Bit zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,IA_Bit>::s_type()) {
	IA_Bit	*srcdata = ((IA_VectorI<IA_Point<int>,IA_Bit>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,IA_Bit>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])) (*valp)++; ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data);
#endif

static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	int *	valp = dest_data;

	do {
	    const IA_Bit *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])) (*valp)++; ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//int *const	dest_data = new int[dest_ps.card()];
	int *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const IA_Bit *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])) (*valp)++; ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_sum_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const IA_Bit	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      int *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_sum_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    int	*intermed = new int[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_sum_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_sum_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,int>
backw_sum_inv(const IA_Image<IA_Point<int>,IA_Bit> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    IA_Bit	*const src_data = new IA_Bit[src_ps.card()];

    zero_extend(img, src_ps, (IA_Bit*)src_data, IA_Bit(0));

#if 1
    const int sz = dest_ps.card();
    int	*const dest_data = new int[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_sum_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,int> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,int>	rval =
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
sum(const IA_Image<IA_Point<int>,IA_Bit> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,IA_Bit>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	int *const	dest_data = new int[dest_ps.card()];
	int	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)) (*valp)++; ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,int>
forw_sum_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const IA_Bit *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IA_Bit *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])) (*valp)++; ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,int>
forw_sum_inv(const IA_Image<IA_Point<int>,IA_Bit> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    IA_Bit	*const src_data = new IA_Bit[src_ps.card()];

    zero_extend(img, src_ps, (IA_Bit*)src_data, IA_Bit(0));
    IA_Image<IA_Point<int>,int>	rval =
	forw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
sum(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,IA_Bit> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,IA_Bit>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	int *const	dest_data = new int[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    IA_Bit	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival) dest_data[offset]++; ;
	    }
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, size, 1);
    }
}

//"C#undef NEED_SPARE
// neighborhood IA_Bit max IA_Bit zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_IA_Bit
#define zero_extend_scan_IA_Bit

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const IA_Bit **src,
				 IA_Bit **dest,
				 IA_Bit zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,IA_Bit> *srciter,
				  IA_Bit **dest,
				  IA_Bit zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    IA_Bit	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    IA_Bit	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,IA_Bit> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, IA_Bit *dest_data,
			IA_Bit zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,IA_Bit>::s_type()) {
	IA_Bit	*srcdata = ((IA_VectorI<IA_Point<int>,IA_Bit>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,IA_Bit>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    IA_Bit *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
    IA_Bit *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IA_Bit *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) |= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    IA_Bit *const dest_data);
#endif

static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    IA_Bit *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	IA_Bit *	valp = dest_data;

	do {
	    const IA_Bit *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) |= (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
	IA_Bit *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const IA_Bit *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) |= (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_max_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const IA_Bit	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      IA_Bit *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_max_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    IA_Bit	*intermed = new IA_Bit[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_max_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_max_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,IA_Bit>
backw_max_inv(const IA_Image<IA_Point<int>,IA_Bit> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,IA_Bit>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    IA_Bit	*const src_data = new IA_Bit[src_ps.card()];

    zero_extend(img, src_ps, (IA_Bit*)src_data, IA_Bit(0));

#if 1
    const int sz = dest_ps.card();
    IA_Bit	*const dest_data = new IA_Bit[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_max_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,IA_Bit> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,IA_Bit>	rval =
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,IA_Bit>
max(const IA_Image<IA_Point<int>,IA_Bit> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,IA_Bit>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
	IA_Bit	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) |= img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,IA_Bit>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,IA_Bit>
forw_max_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const IA_Bit *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
    IA_Bit *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IA_Bit *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) |= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,IA_Bit>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,IA_Bit>
forw_max_inv(const IA_Image<IA_Point<int>,IA_Bit> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,IA_Bit>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    IA_Bit	*const src_data = new IA_Bit[src_ps.card()];

    zero_extend(img, src_ps, (IA_Bit*)src_data, IA_Bit(0));
    IA_Image<IA_Point<int>,IA_Bit>	rval =
	forw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,IA_Bit>
max(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,IA_Bit> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,IA_Bit>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	IA_Bit *const	dest_data = new IA_Bit[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    IA_Bit	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] |= ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,IA_Bit>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood IA_Bit min IA_Bit zero=1
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_IA_Bit
#define zero_extend_scan_IA_Bit

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const IA_Bit **src,
				 IA_Bit **dest,
				 IA_Bit zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,IA_Bit> *srciter,
				  IA_Bit **dest,
				  IA_Bit zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    IA_Bit	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    IA_Bit	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,IA_Bit> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, IA_Bit *dest_data,
			IA_Bit zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,IA_Bit>::s_type()) {
	IA_Bit	*srcdata = ((IA_VectorI<IA_Point<int>,IA_Bit>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,IA_Bit>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    IA_Bit *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
    IA_Bit *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IA_Bit *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) &= (base[nbh_offsets[i]]) ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    IA_Bit *const dest_data);
#endif

static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const IA_Bit *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    IA_Bit *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	IA_Bit *	valp = dest_data;

	do {
	    const IA_Bit *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) &= (base[nbh_offsets[i]]) ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
	IA_Bit *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const IA_Bit *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 1;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) &= (base[nbh_offsets[i]]) ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_min_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const IA_Bit	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      IA_Bit *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_min_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    IA_Bit	*intermed = new IA_Bit[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 1;
	    backw_decomp_min_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_min_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,IA_Bit>
backw_min_inv(const IA_Image<IA_Point<int>,IA_Bit> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,IA_Bit>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    IA_Bit	*const src_data = new IA_Bit[src_ps.card()];

    zero_extend(img, src_ps, (IA_Bit*)src_data, IA_Bit(1));

#if 1
    const int sz = dest_ps.card();
    IA_Bit	*const dest_data = new IA_Bit[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 1;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_min_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,IA_Bit> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,IA_Bit>	rval =
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,IA_Bit>
min(const IA_Image<IA_Point<int>,IA_Bit> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,IA_Bit>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
	IA_Bit	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 1;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) &= img(nbh_ip) ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,IA_Bit>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,IA_Bit>
forw_min_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const IA_Bit *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    IA_Bit *const	dest_data = new IA_Bit[dest_ps.card()];
    IA_Bit *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const IA_Bit *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) &= (base[nbh_offsets[i]]) ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,IA_Bit>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,IA_Bit>
forw_min_inv(const IA_Image<IA_Point<int>,IA_Bit> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,IA_Bit>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    IA_Bit	*const src_data = new IA_Bit[src_ps.card()];

    zero_extend(img, src_ps, (IA_Bit*)src_data, IA_Bit(1));
    IA_Image<IA_Point<int>,IA_Bit>	rval =
	forw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,IA_Bit>
min(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,IA_Bit> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,IA_Bit>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	IA_Bit *const	dest_data = new IA_Bit[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 1; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    IA_Bit	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] &= ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,IA_Bit>(dest_ps, dest_data, size, 1);
    }
}

//
// Things that frob unsigned char images
//
// neighborhood int sum u_char zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_u_char
#define zero_extend_scan_u_char

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const u_char **src,
				 u_char **dest,
				 u_char zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,u_char> *srciter,
				  u_char **dest,
				  u_char zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    u_char	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    u_char	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,u_char> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, u_char *dest_data,
			u_char zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,u_char>::s_type()) {
	u_char	*srcdata = ((IA_VectorI<IA_Point<int>,u_char>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,u_char>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data);
#endif

static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	int *	valp = dest_data;

	do {
	    const u_char *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//int *const	dest_data = new int[dest_ps.card()];
	int *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const u_char *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_sum_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const u_char	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      int *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_sum_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    int	*intermed = new int[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_sum_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_sum_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,int>
backw_sum_inv(const IA_Image<IA_Point<int>,u_char> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    u_char	*const src_data = new u_char[src_ps.card()];

    zero_extend(img, src_ps, (u_char*)src_data, u_char(0));

#if 1
    const int sz = dest_ps.card();
    int	*const dest_data = new int[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_sum_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,int> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,int>	rval =
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
sum(const IA_Image<IA_Point<int>,u_char> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,u_char>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	int *const	dest_data = new int[dest_ps.card()];
	int	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) += img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,int>
forw_sum_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const u_char *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const u_char *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,int>
forw_sum_inv(const IA_Image<IA_Point<int>,u_char> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    u_char	*const src_data = new u_char[src_ps.card()];

    zero_extend(img, src_ps, (u_char*)src_data, u_char(0));
    IA_Image<IA_Point<int>,int>	rval =
	forw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
sum(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,u_char> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,u_char>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	int *const	dest_data = new int[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    u_char	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] += ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood u_char max u_char zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_u_char
#define zero_extend_scan_u_char

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const u_char **src,
				 u_char **dest,
				 u_char zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,u_char> *srciter,
				  u_char **dest,
				  u_char zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    u_char	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    u_char	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,u_char> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, u_char *dest_data,
			u_char zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,u_char>::s_type()) {
	u_char	*srcdata = ((IA_VectorI<IA_Point<int>,u_char>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,u_char>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    u_char *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //u_char *const	dest_data = new u_char[dest_ps.card()];
    u_char *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const u_char *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    u_char *const dest_data);
#endif

static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    u_char *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	u_char *	valp = dest_data;

	do {
	    const u_char *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//u_char *const	dest_data = new u_char[dest_ps.card()];
	u_char *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const u_char *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_max_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const u_char	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      u_char *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_max_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    u_char	*intermed = new u_char[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_max_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_max_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,u_char>
backw_max_inv(const IA_Image<IA_Point<int>,u_char> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,u_char>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    u_char	*const src_data = new u_char[src_ps.card()];

    zero_extend(img, src_ps, (u_char*)src_data, u_char(0));

#if 1
    const int sz = dest_ps.card();
    u_char	*const dest_data = new u_char[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_max_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,u_char> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,u_char>	rval =
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,u_char>
max(const IA_Image<IA_Point<int>,u_char> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,u_char>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	u_char *const	dest_data = new u_char[dest_ps.card()];
	u_char	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)>(*valp)) (*valp) = img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,u_char>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,u_char>
forw_max_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const u_char *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    u_char *const	dest_data = new u_char[dest_ps.card()];
    u_char *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const u_char *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,u_char>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,u_char>
forw_max_inv(const IA_Image<IA_Point<int>,u_char> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,u_char>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    u_char	*const src_data = new u_char[src_ps.card()];

    zero_extend(img, src_ps, (u_char*)src_data, u_char(0));
    IA_Image<IA_Point<int>,u_char>	rval =
	forw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,u_char>
max(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,u_char> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,u_char>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	u_char *const	dest_data = new u_char[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    u_char	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival>dest_data[offset]) dest_data[offset] = ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,u_char>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood u_char min u_char zero=255
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_u_char
#define zero_extend_scan_u_char

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const u_char **src,
				 u_char **dest,
				 u_char zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,u_char> *srciter,
				  u_char **dest,
				  u_char zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    u_char	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    u_char	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,u_char> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, u_char *dest_data,
			u_char zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,u_char>::s_type()) {
	u_char	*srcdata = ((IA_VectorI<IA_Point<int>,u_char>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,u_char>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    u_char *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //u_char *const	dest_data = new u_char[dest_ps.card()];
    u_char *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const u_char *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 255
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    u_char *const dest_data);
#endif

static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const u_char *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    u_char *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	u_char *	valp = dest_data;

	do {
	    const u_char *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//u_char *const	dest_data = new u_char[dest_ps.card()];
	u_char *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const u_char *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 255;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_min_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const u_char	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      u_char *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_min_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    u_char	*intermed = new u_char[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 255;
	    backw_decomp_min_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_min_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,u_char>
backw_min_inv(const IA_Image<IA_Point<int>,u_char> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,u_char>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    u_char	*const src_data = new u_char[src_ps.card()];

    zero_extend(img, src_ps, (u_char*)src_data, u_char(255));

#if 1
    const int sz = dest_ps.card();
    u_char	*const dest_data = new u_char[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 255;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_min_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,u_char> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,u_char>	rval =
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,u_char>
min(const IA_Image<IA_Point<int>,u_char> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,u_char>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	u_char *const	dest_data = new u_char[dest_ps.card()];
	u_char	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 255;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)<(*valp)) (*valp) = img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,u_char>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,u_char>
forw_min_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const u_char *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    u_char *const	dest_data = new u_char[dest_ps.card()];
    u_char *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const u_char *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 255;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,u_char>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,u_char>
forw_min_inv(const IA_Image<IA_Point<int>,u_char> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,u_char>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    u_char	*const src_data = new u_char[src_ps.card()];

    zero_extend(img, src_ps, (u_char*)src_data, u_char(255));
    IA_Image<IA_Point<int>,u_char>	rval =
	forw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,u_char>
min(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,u_char> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,u_char>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	u_char *const	dest_data = new u_char[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 255; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    u_char	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival<dest_data[offset]) dest_data[offset] = ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,u_char>(dest_ps, dest_data, size, 1);
    }
}

//
// Things that frob int images
//
// neighborhood int sum int zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_int
#define zero_extend_scan_int

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const int **src,
				 int **dest,
				 int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,int> *srciter,
				  int **dest,
				  int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,int> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, int *dest_data,
			int zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,int>::s_type()) {
	int	*srcdata = ((IA_VectorI<IA_Point<int>,int>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,int>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data);
#endif

static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	int *	valp = dest_data;

	do {
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//int *const	dest_data = new int[dest_ps.card()];
	int *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_sum_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const int	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      int *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_sum_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    int	*intermed = new int[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_sum_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_sum_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,int>
backw_sum_inv(const IA_Image<IA_Point<int>,int> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(0));

#if 1
    const int sz = dest_ps.card();
    int	*const dest_data = new int[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_sum_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,int> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,int>	rval =
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
sum(const IA_Image<IA_Point<int>,int> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	int *const	dest_data = new int[dest_ps.card()];
	int	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) += img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,int>
forw_sum_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const int *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,int>
forw_sum_inv(const IA_Image<IA_Point<int>,int> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(0));
    IA_Image<IA_Point<int>,int>	rval =
	forw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
sum(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,int> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	int *const	dest_data = new int[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    int	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] += ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood int product int zero=1
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_int
#define zero_extend_scan_int

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const int **src,
				 int **dest,
				 int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,int> *srciter,
				  int **dest,
				  int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,int> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, int *dest_data,
			int zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,int>::s_type()) {
	int	*srcdata = ((IA_VectorI<IA_Point<int>,int>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,int>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) *= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data);
#endif

static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	int *	valp = dest_data;

	do {
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) *= (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//int *const	dest_data = new int[dest_ps.card()];
	int *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 1;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) *= (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_product_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const int	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      int *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_product_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    int	*intermed = new int[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 1;
	    backw_decomp_product_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_product_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,int>
backw_product_inv(const IA_Image<IA_Point<int>,int> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(1));

#if 1
    const int sz = dest_ps.card();
    int	*const dest_data = new int[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 1;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_product_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_product_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,int> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,int>	rval =
	backw_product_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
product(const IA_Image<IA_Point<int>,int> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_product_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	int *const	dest_data = new int[dest_ps.card()];
	int	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 1;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) *= img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,int>
forw_product_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const int *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) *= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,int>
forw_product_inv(const IA_Image<IA_Point<int>,int> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(1));
    IA_Image<IA_Point<int>,int>	rval =
	forw_product_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
product(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,int> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_product_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	int *const	dest_data = new int[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 1; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    int	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] *= ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood int max int zero=-MAXINT
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_int
#define zero_extend_scan_int

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const int **src,
				 int **dest,
				 int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,int> *srciter,
				  int **dest,
				  int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,int> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, int *dest_data,
			int zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,int>::s_type()) {
	int	*srcdata = ((IA_VectorI<IA_Point<int>,int>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,int>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = -MAXINT
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data);
#endif

static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	int *	valp = dest_data;

	do {
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//int *const	dest_data = new int[dest_ps.card()];
	int *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = -MAXINT;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_max_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const int	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      int *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_max_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    int	*intermed = new int[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = -MAXINT;
	    backw_decomp_max_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_max_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,int>
backw_max_inv(const IA_Image<IA_Point<int>,int> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(-MAXINT));

#if 1
    const int sz = dest_ps.card();
    int	*const dest_data = new int[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = -MAXINT;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_max_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,int> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,int>	rval =
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
max(const IA_Image<IA_Point<int>,int> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	int *const	dest_data = new int[dest_ps.card()];
	int	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = -MAXINT;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)>(*valp)) (*valp) = img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,int>
forw_max_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const int *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = -MAXINT;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,int>
forw_max_inv(const IA_Image<IA_Point<int>,int> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(-MAXINT));
    IA_Image<IA_Point<int>,int>	rval =
	forw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
max(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,int> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	int *const	dest_data = new int[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = -MAXINT; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    int	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival>dest_data[offset]) dest_data[offset] = ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood int min int zero=MAXINT
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_int
#define zero_extend_scan_int

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const int **src,
				 int **dest,
				 int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,int> *srciter,
				  int **dest,
				  int zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    int	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,int> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, int *dest_data,
			int zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,int>::s_type()) {
	int	*srcdata = ((IA_VectorI<IA_Point<int>,int>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,int>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = MAXINT
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data);
#endif

static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const int *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    int *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	int *	valp = dest_data;

	do {
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//int *const	dest_data = new int[dest_ps.card()];
	int *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const int *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = MAXINT;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_min_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const int	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      int *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_min_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    int	*intermed = new int[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = MAXINT;
	    backw_decomp_min_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_min_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,int>
backw_min_inv(const IA_Image<IA_Point<int>,int> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(MAXINT));

#if 1
    const int sz = dest_ps.card();
    int	*const dest_data = new int[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = MAXINT;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_min_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,int> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,int>	rval =
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
min(const IA_Image<IA_Point<int>,int> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	int *const	dest_data = new int[dest_ps.card()];
	int	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = MAXINT;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)<(*valp)) (*valp) = img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,int>
forw_min_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const int *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    int *const	dest_data = new int[dest_ps.card()];
    int *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const int *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = MAXINT;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,int>
forw_min_inv(const IA_Image<IA_Point<int>,int> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,int>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    int	*const src_data = new int[src_ps.card()];

    zero_extend(img, src_ps, (int*)src_data, int(MAXINT));
    IA_Image<IA_Point<int>,int>	rval =
	forw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,int>
min(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,int> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,int>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	int *const	dest_data = new int[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = MAXINT; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    int	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival<dest_data[offset]) dest_data[offset] = ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,int>(dest_ps, dest_data, size, 1);
    }
}

//
// Things that frob float images
//
// neighborhood float sum float zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_float
#define zero_extend_scan_float

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const float **src,
				 float **dest,
				 float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,float> *srciter,
				  float **dest,
				  float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,float> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, float *dest_data,
			float zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,float>::s_type()) {
	float	*srcdata = ((IA_VectorI<IA_Point<int>,float>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,float>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data);
#endif

static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	float *	valp = dest_data;

	do {
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//float *const	dest_data = new float[dest_ps.card()];
	float *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_sum_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const float	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      float *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_sum_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    float	*intermed = new float[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_sum_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_sum_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,float>
backw_sum_inv(const IA_Image<IA_Point<int>,float> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(0));

#if 1
    const int sz = dest_ps.card();
    float	*const dest_data = new float[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_sum_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,float> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,float>	rval =
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
sum(const IA_Image<IA_Point<int>,float> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	float *const	dest_data = new float[dest_ps.card()];
	float	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) += img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,float>
forw_sum_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const float *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,float>
forw_sum_inv(const IA_Image<IA_Point<int>,float> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(0));
    IA_Image<IA_Point<int>,float>	rval =
	forw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
sum(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,float> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	float *const	dest_data = new float[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    float	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] += ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood float product float zero=1
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_float
#define zero_extend_scan_float

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const float **src,
				 float **dest,
				 float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,float> *srciter,
				  float **dest,
				  float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,float> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, float *dest_data,
			float zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,float>::s_type()) {
	float	*srcdata = ((IA_VectorI<IA_Point<int>,float>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,float>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) *= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data);
#endif

static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	float *	valp = dest_data;

	do {
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) *= (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//float *const	dest_data = new float[dest_ps.card()];
	float *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 1;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) *= (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_product_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const float	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      float *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_product_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    float	*intermed = new float[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 1;
	    backw_decomp_product_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_product_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,float>
backw_product_inv(const IA_Image<IA_Point<int>,float> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(1));

#if 1
    const int sz = dest_ps.card();
    float	*const dest_data = new float[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 1;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_product_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_product_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,float> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,float>	rval =
	backw_product_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
product(const IA_Image<IA_Point<int>,float> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_product_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	float *const	dest_data = new float[dest_ps.card()];
	float	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 1;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) *= img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,float>
forw_product_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const float *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) *= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,float>
forw_product_inv(const IA_Image<IA_Point<int>,float> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(1));
    IA_Image<IA_Point<int>,float>	rval =
	forw_product_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
product(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,float> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_product_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	float *const	dest_data = new float[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 1; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    float	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] *= ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood float max float zero=-infinity()
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_float
#define zero_extend_scan_float

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const float **src,
				 float **dest,
				 float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,float> *srciter,
				  float **dest,
				  float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,float> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, float *dest_data,
			float zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,float>::s_type()) {
	float	*srcdata = ((IA_VectorI<IA_Point<int>,float>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,float>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = -infinity()
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data);
#endif

static void
backw_max_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	float *	valp = dest_data;

	do {
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//float *const	dest_data = new float[dest_ps.card()];
	float *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = -infinity();
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_max_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const float	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      float *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_max_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    float	*intermed = new float[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = -infinity();
	    backw_decomp_max_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_max_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,float>
backw_max_inv(const IA_Image<IA_Point<int>,float> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(-infinity()));

#if 1
    const int sz = dest_ps.card();
    float	*const dest_data = new float[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = -infinity();
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_max_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,float> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,float>	rval =
	backw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
max(const IA_Image<IA_Point<int>,float> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	float *const	dest_data = new float[dest_ps.card()];
	float	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = -infinity();
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)>(*valp)) (*valp) = img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,float>
forw_max_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const float *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = -infinity();
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])>(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,float>
forw_max_inv(const IA_Image<IA_Point<int>,float> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(-infinity()));
    IA_Image<IA_Point<int>,float>	rval =
	forw_max_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
max(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,float> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_max_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	float *const	dest_data = new float[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = -infinity(); ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    float	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival>dest_data[offset]) dest_data[offset] = ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood float min float zero=infinity()
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_float
#define zero_extend_scan_float

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const float **src,
				 float **dest,
				 float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,float> *srciter,
				  float **dest,
				  float zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    float	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,float> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, float *dest_data,
			float zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,float>::s_type()) {
	float	*srcdata = ((IA_VectorI<IA_Point<int>,float>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,float>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = infinity()
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data);
#endif

static void
backw_min_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const float *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    float *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	float *	valp = dest_data;

	do {
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//float *const	dest_data = new float[dest_ps.card()];
	float *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const float *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = infinity();
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_min_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const float	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      float *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_min_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    float	*intermed = new float[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = infinity();
	    backw_decomp_min_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_min_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,float>
backw_min_inv(const IA_Image<IA_Point<int>,float> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(infinity()));

#if 1
    const int sz = dest_ps.card();
    float	*const dest_data = new float[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = infinity();
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_min_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,float> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,float>	rval =
	backw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
min(const IA_Image<IA_Point<int>,float> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	float *const	dest_data = new float[dest_ps.card()];
	float	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = infinity();
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		if (img(nbh_ip)<(*valp)) (*valp) = img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,float>
forw_min_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const float *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    float *const	dest_data = new float[dest_ps.card()];
    float *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const float *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = infinity();
	// ;

	for (i=0; i<nbh_sz; i++) {
	    if ((base[nbh_offsets[i]])<(*valp)) (*valp) = (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,float>
forw_min_inv(const IA_Image<IA_Point<int>,float> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,float>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    float	*const src_data = new float[src_ps.card()];

    zero_extend(img, src_ps, (float*)src_data, float(infinity()));
    IA_Image<IA_Point<int>,float>	rval =
	forw_min_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,float>
min(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,float> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,float>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_min_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	float *const	dest_data = new float[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = infinity(); ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    float	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		if (ival<dest_data[offset]) dest_data[offset] = ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,float>(dest_ps, dest_data, size, 1);
    }
}

//
// Things that frob complex images
//
// neighborhood complex sum complex zero=0
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_complex
#define zero_extend_scan_complex

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const complex **src,
				 complex **dest,
				 complex zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,complex> *srciter,
				  complex **dest,
				  complex zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    complex	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    complex	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,complex> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, complex *dest_data,
			complex zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,complex>::s_type()) {
	complex	*srcdata = ((IA_VectorI<IA_Point<int>,complex>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,complex>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const complex *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    complex *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //complex *const	dest_data = new complex[dest_ps.card()];
    complex *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const complex *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const complex *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    complex *const dest_data);
#endif

static void
backw_sum_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const complex *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    complex *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	complex *	valp = dest_data;

	do {
	    const complex *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//complex *const	dest_data = new complex[dest_ps.card()];
	complex *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const complex *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 0;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) += (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_sum_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const complex	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      complex *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_sum_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    complex	*intermed = new complex[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 0;
	    backw_decomp_sum_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_sum_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,complex>
backw_sum_inv(const IA_Image<IA_Point<int>,complex> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,complex>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    complex	*const src_data = new complex[src_ps.card()];

    zero_extend(img, src_ps, (complex*)src_data, complex(0));

#if 1
    const int sz = dest_ps.card();
    complex	*const dest_data = new complex[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 0;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_sum_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,complex> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,complex>	rval =
	backw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,complex>
sum(const IA_Image<IA_Point<int>,complex> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,complex>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	complex *const	dest_data = new complex[dest_ps.card()];
	complex	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 0;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) += img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,complex>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,complex>
forw_sum_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const complex *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    complex *const	dest_data = new complex[dest_ps.card()];
    complex *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const complex *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 0;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) += (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,complex>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,complex>
forw_sum_inv(const IA_Image<IA_Point<int>,complex> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,complex>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    complex	*const src_data = new complex[src_ps.card()];

    zero_extend(img, src_ps, (complex*)src_data, complex(0));
    IA_Image<IA_Point<int>,complex>	rval =
	forw_sum_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,complex>
sum(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,complex> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,complex>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_sum_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	complex *const	dest_data = new complex[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 0; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    complex	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] += ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,complex>(dest_ps, dest_data, size, 1);
    }
}

// neighborhood complex product complex zero=1
// Emacs: -*- C++ -*-

//
//	Copyright 1993, Center for Computer Vision and Visualization,
//	University of Florida.  All rights reserved.
//


//
// $Log: nbh-reduction,v $
// Revision 1.4.1.3  1995/01/13  19:37:42  thoth
// CoreImage has been re-merged with Image.  All special stuff should be
// friends of FBI now.
//
// Revision 1.4.1.2  1995/01/09  18:20:14  thoth
// Boxy pointset constructor replaced by function.
//
// Revision 1.4.1.1  1994/12/28  17:57:21  thoth
// Detect dimension mismatch a little earlier.
//
// Revision 1.4  1994/11/14  15:53:48  thoth
// Fixed severe uninitialized data error introduced to variant reductions
// during the autodecomposition experiments.
//
// Revision 1.3  1994/07/25  17:26:39  thoth
// Name sanitization
//
// Revision 1.2  1994/05/08  19:43:05  thoth
// New automatic neighborhood decomposition capability.
//
// Revision 1.1  1994/01/31  16:50:30  thoth
// Initial revision
//

#if 0
#ifndef zero_extend_scan_complex
#define zero_extend_scan_complex

static void zero_extend_vec_scan(const IA_SetStructure &ss,
				 const complex **src,
				 complex **dest,
				 complex zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    (*src) += temp.count;
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    for (unsigned j=0; j<temp.count; j++) {
		*((*dest)++) = *((*src)++);
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_vec_scan(temp.substructure, src, dest, zero);
	}
    }
}

static void zero_extend_iter_scan(const IA_SetStructure &ss,
				  IA_IVIter<IA_Point<int>,complex> *srciter,
				  complex **dest,
				  complex zero)
{
    for (unsigned i=0; i<ss.nintervals(); i++) {
	IA_ss_interval	temp(ss.retrieve_interval(i));
	if (temp.substructure == IA_SetStructure::FIRST_ONLY) {
	    complex	blah;
	    for (unsigned j=0; j<temp.count; j++) 
		(*srciter)(blah);
	} else if (temp.substructure == IA_SetStructure::BOTH) {
	    complex	blah;
	    for (unsigned j=0; j<temp.count; j++) {
		(*srciter)(blah);
		*((*dest)++) = blah;
	    }
	} else if (temp.substructure == IA_SetStructure::SECOND_ONLY) {
	    for (unsigned j=0; j<temp.count; j++)
		*((*dest)++) = zero;
	} else {
	    for (unsigned j=0; j<temp.count; j++)
		zero_extend_iter_scan(temp.substructure, srciter, dest, zero);
	}
    }
}

static void zero_extend(const IA_Image<IA_Point<int>,complex> &srcimg,
			const IA_Set<IA_Point<int> > &dest_ps, complex *dest_data,
			complex zero)
{
    IA_SetStructure	ss;
    intersect_with_dualstruct(srcimg.domain(), dest_ps, &ss);

    if (srcimg.type() == IA_VectorI<IA_Point<int>,complex>::s_type()) {
	complex	*srcdata = ((IA_VectorI<IA_Point<int>,complex>*)srcimg.bip)->vec;
	zero_extend_vec_scan(ss, &srcdata, &dest_data, zero);
    } else {
	IA_IVIter<IA_Point<int>,complex>	iter(srcimg);
	zero_extend_iter_scan(ss, &iter, &dest_data, zero);
    }
}

#endif
#endif

//
// backward neighborhood reductions
//

#ifdef NEED_SPARE
static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const complex *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    complex *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    //complex *const	dest_data = new complex[dest_ps.card()];
    complex *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const complex *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1
	//  ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) *= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    //return dest_data;
}
#else
static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const complex *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    complex *const dest_data);
#endif

static void
backw_product_inv_core(IA_Point<int> src_infimum,
		    IA_Point<int> src_width,
		    const complex *src_data, // length is prod(src_width)
		    const IA_Set<IA_Point<int> > &nbh,
		    IA_Set<IA_Point<int> > dest_ps,
		    complex *const dest_data)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan += ip[i];
	    }
	    o_scan++;
	}
    }

    if (dest_ps.boxy()) {

	IA_Point<int>	inf_ = dest_ps.inf();
	IA_Point<int>	sup_ = dest_ps.sup();
	IA_Point<int>	dest_width = sup_-inf_+1;

	int	offset = inf_ [0] - src_infimum[0];
	int	i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += inf_[i] - src_infimum[i];
	}

	IA_Point<int>	bases = extend_to_point(0, inf_.dim());
	i = dimen;
	bases[i-1] = 1;
	for (i--; i>0; i--) {
	    bases[i-1] = bases[i]*src_width[i];
	}

	IA_Point<int>	ip = extend_to_point(0, inf_.dim());

	complex *	valp = dest_data;

	do {
	    const complex *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    for (i=0; i<nbh_sz; i++) {
		(*valp) *= (base[nbh_offsets[i]]); ;
	    }
	    valp++;

	    // now we go to next point
	    for (i=dimen-1; i>=0; i--) {
		ip[i]++;
		offset+=bases[i];
		if (ip[i]<dest_width[i])
		    break;
		ip[i] = 0;
		offset -= dest_width[i]*bases[i];
	    }
	    // no more code here, might corrupt i
	} while (i>=0);

    } else {

	IA_PSIter<IA_Point<int> >	iter(dest_ps);
	IA_Point<int>	ip;
	//complex *const	dest_data = new complex[dest_ps.card()];
	complex *	valp = dest_data;
	while (iter(ip)) {
	    int	offset= ip[0] - src_infimum[0];
	    unsigned i;
	    for (i=1; i<dimen; i++) {
		offset *= src_width[i];
		offset += ip[i] - src_infimum[i];
	    }
	    const complex *const base = src_data + offset;

	    // _IVAL_=(base[nbh_offsets[i]])
	    // _IRESULT_=(*valp)

	    *valp = 1;
	    //  ;

	    for (i=0; i<nbh_sz; i++) {
		(*valp) *= (base[nbh_offsets[i]]); ;
	    }
	    //{
	    //     ;
	    //}
	    valp++;
	}

    }				// end if boxy()

    delete[] nbh_offsets;

    //return dest_data;
}

static void
backw_decomp_product_inv(IA_Point<int> src_infimum,
		      IA_Point<int> src_width,
		      const complex	*src_data,
		      IA_Set<IA_Point<int> >	invnbh,
		      IA_Set<IA_Point<int> >	dest_ps,
		      complex *dest_data)
{
    const int	sz = dest_ps.card();

    int	i;

    IA_PointSetDecomposition	decomp(invnbh);
    for (i=0; i<decomp.nfragments(); i++) {
	const IA_decomp_frag	&frag = decomp.get_frag(i);
	if (frag.plate.card()==1) {
	    backw_product_inv_core(src_infimum, src_width, src_data,
				       frag.bar, dest_ps, dest_data);
	} else {
	    IA_Point<int>	inf_ = dest_ps.inf() + frag.bar.inf();
	    IA_Point<int>	sup_ = dest_ps.sup() + frag.bar.sup();
	    IA_Set<IA_Point<int> >	intermed_ps = IA_boxy_pset(inf_, sup_);
	    const int sz = intermed_ps.card();
	    complex	*intermed = new complex[sz];
	    for (int j=0; j<sz; j++)
		intermed[j] = 1;
	    backw_decomp_product_inv
		(src_infimum, src_width, src_data,
		 frag.plate, intermed_ps, intermed);
	    backw_product_inv_core(inf_, sup_ - inf_ + 1, intermed,
				frag.bar, dest_ps, dest_data);
	    delete[] intermed;
	}
    }

}

static IA_Image<IA_Point<int>,complex>
backw_product_inv(const IA_Image<IA_Point<int>,complex> &img,
	 const IA_Set<IA_Point<int> > &invnbh,
	 IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,complex>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()+invnbh.inf();
    IA_Point<int>	sup_ = dest_ps.sup()+invnbh.sup();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    complex	*const src_data = new complex[src_ps.card()];

    zero_extend(img, src_ps, (complex*)src_data, complex(1));

#if 1
    const int sz = dest_ps.card();
    complex	*const dest_data = new complex[sz];
    for (int i=0; i<sz; i++)
	dest_data[i] = 1;
    if (IA_Neighborhood<IA_Point<int>, IA_Point<int> >::auto_decomposition)
	backw_decomp_product_inv (inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    else
	backw_product_inv_core(inf_, sup_-inf_ + 1, src_data,
			       invnbh, dest_ps, dest_data);
    IA_Image<IA_Point<int>,complex> rval (dest_ps, dest_data, dest_ps.card(), 1);
#else
    IA_Image<IA_Point<int>,complex>	rval =
	backw_product_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);
#endif

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,complex>
product(const IA_Image<IA_Point<int>,complex> &img_,
     const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,complex>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return backw_product_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*) nbh.bnbh)->value,
	     dest_ps);
    } else {
	complex *const	dest_data = new complex[dest_ps.card()];
	complex	*valp = dest_data;
	IA_PSIter<IA_Point<int> >	dest_iter(dest_ps);
	IA_Point<int>	base_ip;
	while (dest_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;

	    // _IVAL_=img(nbh_ip)
	    // _IRESULT_=(*valp)

#if 1
	    *valp = 1;
#else			// obsoleted by neighborhood reduction decomposition
	     ;
#endif

	    while ( nbh_iter(nbh_ip) ) {
		if (! img.domain().contains(nbh_ip))
		    continue;
		(*valp) *= img(nbh_ip); ;
	    }
	    {
		 ;
	    }
	    valp++;
	}
	return IA_Image<IA_Point<int>,complex>(dest_ps, dest_data,
					   dest_ps.card(), 1);
    }
}

//
// foward neighborhood reductions
//

static IA_Image<IA_Point<int>,complex>
forw_product_inv_core(IA_Point<int> src_infimum,
	      IA_Point<int> src_width,
	      const complex *src_data, // length is prod(src_width)
	      const IA_Set<IA_Point<int> > &nbh,
	      IA_Set<IA_Point<int> > dest_ps)
{
    const int	dimen = src_width.dim();

    int		nbh_sz = nbh.card();
    int	*const nbh_offsets = new int[nbh_sz];

    {
	int	*o_scan = nbh_offsets;
	IA_PSIter<IA_Point<int> >	iter(nbh);
	IA_Point<int>	ip;
	while (iter(ip)) {
	    *o_scan = -ip[0];
	    for (unsigned i=1; i<dimen; i++) {
		*o_scan *= src_width[i];
		*o_scan -= ip[i];
	    }
	    o_scan++;
	}
    }

    IA_PSIter<IA_Point<int> >	iter(dest_ps);
    IA_Point<int>	ip;
    complex *const	dest_data = new complex[dest_ps.card()];
    complex *	valp = dest_data;
    while (iter(ip)) {
	int	offset= ip[0] - src_infimum[0];
	unsigned i;
	for (i=1; i<dimen; i++) {
	    offset *= src_width[i];
	    offset += ip[i] - src_infimum[i];
	}
	const complex *const base = src_data + offset;

	// _IVAL_=(base[nbh_offsets[i]])
	// _IRESULT_=(*valp)

	*valp = 1;
	// ;

	for (i=0; i<nbh_sz; i++) {
	    (*valp) *= (base[nbh_offsets[i]]); ;
	}
	//{
	//     ;
	//}
	valp++;
    }
    delete[] nbh_offsets;

    return IA_Image<IA_Point<int>,complex>(dest_ps, dest_data, dest_ps.card(), 1);
}

static
IA_Image<IA_Point<int>,complex>
forw_product_inv(const IA_Image<IA_Point<int>,complex> &img,
	const IA_Set<IA_Point<int> > &invnbh,
	IA_Set<IA_Point<int> > dest_ps)
{
    if (img.domain().dim() != invnbh.dim()) {
	ia_throw(Neighborhood_DimensionMismatch2_Exception(__FILE__,__LINE__));
	return IA_Image<IA_Point<int>,complex>();
    }

    IA_Point<int>	inf_ = dest_ps.inf()-invnbh.sup();
    IA_Point<int>	sup_ = dest_ps.sup()-invnbh.inf();
    IA_Set<IA_Point<int> >	src_ps = IA_boxy_pset(inf_,sup_);

    complex	*const src_data = new complex[src_ps.card()];

    zero_extend(img, src_ps, (complex*)src_data, complex(1));
    IA_Image<IA_Point<int>,complex>	rval =
	forw_product_inv_core(inf_, sup_-inf_ + 1, src_data, invnbh, dest_ps);

    delete[] src_data;

    return rval;
}

IA_Image<IA_Point<int>,complex>
product(const IA_Neighborhood<IA_Point<int>,IA_Point<int> > &nbh,
     const IA_Image<IA_Point<int>,complex> &img_,
     IA_Set<IA_Point<int> > dest_ps)
{
    IA_Image<IA_Point<int>,complex>	img(img_);

    if (nbh.type() == IA_InvariantNbh<IA_Point<int>,IA_Point<int> >::s_type()) {
	return forw_product_inv
	    (img,
	     ((IA_InvariantNbh<IA_Point<int>,IA_Point<int> >*)nbh.bnbh)->value,
	     dest_ps);
    } else {
	unsigned	size = dest_ps.card();
	complex *const	dest_data = new complex[size];

	for (int i=0; i<size; i++) {
	    // _IRESULT_=dest_data[i]
	    dest_data[i] = 1; ;
	}

	IA_PSIter<IA_Point<int> >	src_iter(img.domain());
	IA_Point<int>	base_ip;
	while (src_iter(base_ip)) {
	    IA_Set<IA_Point<int> >	ps = nbh(base_ip);

	    IA_PSIter<IA_Point<int> >	nbh_iter(ps);
	    IA_Point<int>	nbh_ip;
	    complex	ival = img(base_ip);

	    // _IVAL_=ival
	    // _IRESULT_=dest_data[offset]

	    while ( nbh_iter(nbh_ip) ) {
		if (! dest_ps.contains(nbh_ip))
		    continue;
		unsigned	offset = dest_ps.index(nbh_ip);
//		cout << "nbh(" << base_ip << ") =  " << tv << " and nbh()("
//		     << nbh_ip <<") = " << nbh_val << endl;
		dest_data[offset] *= ival; ;
	    }
	}
	return IA_Image<IA_Point<int>,complex>(dest_ps, dest_data, size, 1);
    }
}

