ATYPE vec_vec_bo_OPNAME(const IA_VectorDI<LTYPE> *lhs,const IA_VectorDI<RTYPE> *rhs)
{
    const IA_IntPointSet domain = lhs->domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	const LTYPE *ls = lhs->vec;
	const RTYPE *rs = rhs->vec;
	for (unsigned i=0; i<sz; i++) {
	    if (!SUBOP(*(ls++), *(rs++)))
		return 0;
	}
    }
    return 1;
}

#ifndef vec_scalar_ATYPE_LTYPE_RTYPE_OPNAME
#define vec_scalar_ATYPE_LTYPE_RTYPE_OPNAME
ATYPE vec_scalar_bo_OPNAME(const IA_VectorDI<LTYPE> *lhs,RTYPE rhs)
{
    const IA_IntPointSet domain = lhs->domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	const LTYPE *ls = lhs->vec;
	for (unsigned i=0; i<sz; i++) {
	    if (!SUBOP(*(ls++), rhs))
		return 0;
	}
    }
    return 1;
}
#endif // vec_scalar_ATYPE_LTYPE_RTYPE_OPNAME

ATYPE vec_iter_bo_OPNAME(const IA_VectorDI<LTYPE> *lhs,IA_DiscreteImage<RTYPE> rhs)
{
    const IA_IntPointSet domain = lhs->domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	const LTYPE *ls = lhs->vec;
	RTYPE rtmp;
	IA_DIVIter<RTYPE>	riter(rhs);
	for (unsigned i=0; i<sz; i++) {
	    riter(rtmp);
	    if (!SUBOP(*(ls++), rtmp))
		return 0;
	}
    }
    return 1;
}

#ifndef scalar_vec_ATYPE_LTYPE_RTYPE_OPNAME
#define scalar_vec_ATYPE_LTYPE_RTYPE_OPNAME
ATYPE scalar_vec_bo_OPNAME(LTYPE lhs,const IA_VectorDI<RTYPE> *rhs)
{
    const IA_IntPointSet domain = rhs->domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	const RTYPE *rs = rhs->vec;
	for (unsigned i=0; i<sz; i++) {
	    if (!SUBOP(lhs, *(rs++)))
		return 0;
	}
    }
    return 1;
}
#endif // scalar_vec_ATYPE_LTYPE_RTYPE_OPNAME

// scalar-scalar operation is trivial

#ifndef scalar_iter_ATYPE_LTYPE_RTYPE_OPNAME
#define scalar_iter_ATYPE_LTYPE_RTYPE_OPNAME
ATYPE scalar_iter_bo_OPNAME(LTYPE lhs,IA_DiscreteImage<RTYPE> rhs)
{
    const IA_IntPointSet domain = rhs.domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	RTYPE rtmp;
	IA_DIVIter<RTYPE>	riter(rhs);
	for (unsigned i=0; i<sz; i++) {
	    riter(rtmp);
	    if (!SUBOP(lhs, rtmp))
		return 0;
	}
    }
    return 1;
}
#endif // scalar_iter_ATYPE_LTYPE_RTYPE_OPNAME

ATYPE iter_vec_bo_OPNAME(IA_DiscreteImage<LTYPE> lhs,const IA_VectorDI<RTYPE> *rhs)
{
    const IA_IntPointSet domain = lhs.domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	LTYPE ltmp;
	IA_DIVIter<LTYPE>	liter(lhs);
	const RTYPE *rs = rhs->vec;
	for (unsigned i=0; i<sz; i++) {
	    liter(ltmp);
	    if (!SUBOP(ltmp, *(rs++)))
		return 0;
	}
    }
    return 1;
}

#ifndef iter_scalar_ATYPE_LTYPE_RTYPE_OPNAME
#define iter_scalar_ATYPE_LTYPE_RTYPE_OPNAME
ATYPE iter_scalar_bo_OPNAME(IA_DiscreteImage<LTYPE> lhs,RTYPE rhs)
{
    const IA_IntPointSet domain = lhs.domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	LTYPE ltmp;
	IA_DIVIter<LTYPE>	liter(lhs);
	for (unsigned i=0; i<sz; i++) {
	    liter(ltmp);
	    if (!SUBOP(ltmp, rhs))
		return 0;
	}
    }
    return 1;
}
#endif // iter_scalar_ATYPE_LTYPE_RTYPE_OPNAME

ATYPE iter_iter_bo_OPNAME(IA_DiscreteImage<LTYPE> lhs,IA_DiscreteImage<RTYPE> rhs)
{
    const IA_IntPointSet domain = lhs.domain();
    const unsigned sz = domain.card();
    ATYPE *const vec = new ATYPE[sz];

    {
	ATYPE *ds = vec;
	LTYPE ltmp;
	IA_DIVIter<LTYPE>	liter(lhs);
	RTYPE rtmp;
	IA_DIVIter<RTYPE>	riter(rhs);
	for (unsigned i=0; i<sz; i++) {
	    liter(ltmp);
	    riter(rtmp);
	    if (!SUBOP(ltmp, rtmp))
		return 0;
	}
    }
    return 1;
}

ATYPE OP ( const IA_DiscreteImage<LTYPE> &lhs,
	      const IA_DiscreteImage<RTYPE> &rhs)
{
    if (lhs.domain() != rhs.domain()) {
	static ATYPE	rval;
	ia_throw( IA::PSET_MISMATCH, __FILE__,__LINE__);
	return rval;
    }

    if (lhs.type() == IA_VectorDI<LTYPE>::s_type()) {
	if (rhs.type() == IA_VectorDI<RTYPE>::s_type()) {
	    return vec_vec_bo_OPNAME((IA_VectorDI<LTYPE> *)lhs.bdip,(IA_VectorDI<RTYPE> *)rhs.bdip);
	} else if (rhs.type() == IA_ConstDI<RTYPE>::s_type()) {
	    return vec_scalar_bo_OPNAME((IA_VectorDI<LTYPE> *)lhs.bdip,((IA_ConstDI<RTYPE> *)rhs.bdip)->value);
	} else {
	    return vec_iter_bo_OPNAME((IA_VectorDI<LTYPE> *)lhs.bdip,rhs);
	}
    } else if (lhs.type() == IA_ConstDI<LTYPE>::s_type()) {
	if (rhs.type() == IA_VectorDI<RTYPE>::s_type()) {
	    return scalar_vec_bo_OPNAME(((IA_ConstDI<LTYPE> *)lhs.bdip)->value,(IA_VectorDI<RTYPE> *)rhs.bdip);
	} else if (rhs.type() == IA_ConstDI<RTYPE>::s_type()) {
	    return SUBOP(((IA_ConstDI<LTYPE> *)lhs.bdip)->value, ((IA_ConstDI<RTYPE> *)rhs.bdip)->value);
	} else {
	    return scalar_iter_bo_OPNAME(((IA_ConstDI<LTYPE> *)lhs.bdip)->value,rhs);
	}
    } else {
	if (rhs.type() == IA_VectorDI<RTYPE>::s_type()) {
	    return iter_vec_bo_OPNAME(lhs,(IA_VectorDI<RTYPE> *)rhs.bdip);
	} else if (rhs.type() == IA_ConstDI<RTYPE>::s_type()) {
	    return iter_scalar_bo_OPNAME(lhs,((IA_ConstDI<RTYPE> *)rhs.bdip)->value);
	} else {
	    return iter_iter_bo_OPNAME(lhs,rhs);
	}
    }
}
