// Emacs: -*- C++ -*-

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


//
// $Log: AuxImage.c,v $
// Revision 1.2  1994/02/01  17:01:37  thoth
// Fix some screwy instantiation problems.
//
// Revision 1.1  1994/01/31  16:43:38  thoth
// Initial revision
//

#include "Point.h"

#include "Set.h"
#include "IntPointSet.h"
#include "FloatPointSet.h"
#include "ClosurePS.h"

#include "VectorI.h"
#include "ConstI.h"

#include "CoreImage.h"
#include "ClosureI.h"
#include "ImageIter.h"
#include "image_errors.h"

//
//  The chi_contains function between an image and a valueset.
//

template <class T>
IA_CoreImage<IA_IntPoint, IA_Bit>
vec_chi_contains(const IA_VectorI<IA_IntPoint, T> *lhs, const IA_ValueSet<T> &rhs)
{
    const IA_Set<IA_IntPoint> domain = lhs->domain();
    const unsigned sz = domain.card();
    IA_Bit *const vec = new IA_Bit[sz];

    {
	IA_Bit *ds = vec;
	const T *ls = lhs->vec;
	for (unsigned i=0; i<sz; i++) {
	    *(ds++) = rhs.contains(*ls);
	    ls++;
	}
    }
    return IA_CoreImage<IA_IntPoint, IA_Bit>(domain, vec, sz, 1);
}


// scalar-scalar operation is trivial


template <class T>
IA_CoreImage<IA_IntPoint, IA_Bit>
iter_chi_contains(const IA_CoreImage<IA_IntPoint, T> &lhs, const IA_ValueSet<T> &rhs)
{
    const IA_Set<IA_IntPoint> domain = lhs.domain();
    const unsigned sz = domain.card();
    IA_Bit *const vec = new IA_Bit[sz];

    {
	IA_Bit *ds = vec;
	T ltmp;
	IA_IVIter<IA_IntPoint, T>	liter(lhs);
	for (unsigned i=0; i<sz; i++) {
	    liter(ltmp);
	    *(ds++) = rhs.contains(ltmp);
	}
    }
    return IA_CoreImage<IA_IntPoint, IA_Bit>(domain, vec, sz, 1);
}


template <class T>
IA_CoreImage<IA_IntPoint, IA_Bit>
chi_contains ( const IA_CoreImage<IA_IntPoint, T> &lhs, const IA_ValueSet<T> &rhs)
{
    if (lhs.type() == IA_VectorI<IA_IntPoint, T>::s_type()) {
	return vec_chi_contains((IA_VectorI<IA_IntPoint, T> *)lhs.bip,rhs);
    } else if (lhs.type() == IA_ConstI<IA_IntPoint, T>::s_type()) {
	return IA_CoreImage<IA_IntPoint, IA_Bit> (lhs.domain(), rhs.contains(((IA_ConstI<IA_IntPoint, T> *)lhs.bip)->value));
    } else {
	return iter_chi_contains(lhs,rhs);
    }
}

//
//
//

// transpose of a boxy 2D image

template <class T>
IA_CoreImage<IA_IntPoint, T>
vec_transpose(const IA_VectorI<IA_IntPoint, T> *img)
{
    const IA_IntPointSet domain = img->domain();
    const unsigned sz = domain.card();
    T *const vec = new T[sz];
    unsigned	width = domain.sup()[0] - domain.inf()[0] + 1;
    unsigned	height = domain.sup()[1] - domain.inf()[1] + 1;

    {
	const T *p = img->vec;
	for (unsigned x=0; x<width; x++) {
	    for (unsigned y=0; y<height; y++) {
		vec[y*width + x] = p[x*height + y];
	    }
	}
    }
    return IA_CoreImage<IA_IntPoint, T>(transpose(domain), vec, sz, 1);
}

template <class T>
IA_CoreImage<IA_IntPoint, T>
iter_transpose(const IA_CoreImage<IA_IntPoint, T> &img)
{
    const IA_IntPointSet domain = img.domain();
    const unsigned sz = domain.card();
    T *const vec = new T[sz];
    unsigned	width = domain.sup()[0] - domain.inf()[0] + 1;
    unsigned	height = domain.sup()[1] - domain.inf()[1] + 1;

    {
	T ltmp;
	IA_IVIter<IA_IntPoint, T>	liter(img);
	for (unsigned x=0; x<width; x++) {
	    for (unsigned y=0; y<height; y++) {
		liter(ltmp);
		vec[y*width + x] = ltmp;
	    }
	}
    }
    return IA_CoreImage<IA_IntPoint, T>(transpose(domain), vec, sz, 1);
}

template <class T> IA_CoreImage<IA_IntPoint,T>
transpose(const IA_CoreImage<IA_IntPoint, T> & img)
{
    if (!img.domain().boxy()
	|| img.domain().dim()!=2) {
	ia_throw(Image_BadTranspose_Exception(__FILE__, __LINE__));
	return IA_CoreImage<IA_IntPoint,T>();
    }

    if (img.type() == IA_VectorI<IA_IntPoint, T>::s_type()) {
	return vec_transpose((IA_VectorI<IA_IntPoint, T> *)img.bip);

    } else if (img.type() == IA_ConstI<IA_IntPoint, T>::s_type()) {
	return IA_CoreImage<IA_IntPoint, T>
	    (transpose(img.domain()),
	     ((IA_ConstI<IA_IntPoint, T> *)img.bip)->value);

    } else {
	return iter_transpose(img);
    }
}

//
//
//

template <class Q, class P>
class IA_PPCompositionPS: public IA_ClosurePS<P> {
    // This class is used to define a pointset that contains
    // a point of type P if the pointset "base" contains f(p).
    // It is a Point-Point composition PointSet.
    Q	(*f)(const P&);
    IA_Set<Q>	base;
  public:
    IA_PPCompositionPS(Q (*f_)(const P&),
			const IA_Set<Q> &base_)
    :IA_ClosurePS<P>(base_.dim()), f(f_), base(base_) {
    }
    IA_ClosurePS<P> *clone_self() const {
	return new IA_PPCompositionPS(*this);
    }
    // given:
    // c = compose(ps, f);
    // then
    // c.contains(p)   iff ps.contains(f(p))
    int contains(const P &ip) const {
	// Uglier than Rosanne bug
	Q (*f2)(const P&) = f;
	return base.contains(f2(ip));
    }
};

template <class P, class Q, class T>
class IA_PPCompositionI: public IA_ClosureI<P,T> {
    //  This defines an image whose values are taken
    // from a source image after applying a function to the point.
    // It is a Point-Point composition image.
    Q	(*f)(const P&);
    IA_CoreImage<Q,T>	im;
  public:
    IA_PPCompositionI( const IA_CoreImage<Q,T> &im_,
		       Q (*f_)(const P&))
    :IA_ClosureI<P,T>(IA_PPCompositionPS<Q,P>(f_, im_.domain())),
     f(f_), im(im_) { }
    // given:
    // c = compose(i, f);
    // then
    // c(p) is defined by i(f(p))
    T operator()(const P&p) const {
	Q (*f2)(const P &) = f;
	return im(f2(p));
    }
    IA_ClosureI<P,T>* clone_self() const {
	return new IA_PPCompositionI<P,Q,T>(*this);
    }
#if 0
    T *range(unsigned *n) const {
	return im.range(n);
    }
#endif
};

template <class P, class T>
IA_CoreImage<P,T> compose(const IA_CoreImage<P,T> &i,
		      P (*f)(const P&))
{
    return IA_PPCompositionI<P,P,T>(i, f);
}

//
//
//

#include "Closure.h"

template <class Q, class P>
class IA_PPCompClosurePS: public IA_ClosurePS<P> {
    IA_Closure< P,Q> *f;
    IA_Set<Q>	base;
  public:
    IA_PPCompClosurePS( const IA_Closure< P,Q> &f_,
			const IA_Set<Q> &base_)
    :IA_ClosurePS<P>(base_.dim()), f(f_.clone_self()), base(base_) {
    }
    IA_PPCompClosurePS(const IA_PPCompClosurePS &other)
    :IA_ClosurePS<P>(other.base.dim()), f(other.f->clone_self()),
     base(other.base) {
	 // f has been cloned
    }
    ~IA_PPCompClosurePS() { delete f; }

    IA_ClosurePS<P> *clone_self() const {
	return new IA_PPCompClosurePS(*this);
    }
    int contains(const P &ip) const {
	return base.contains((*f)(ip));
    }
};

template <class P, class Q, class T>
class IA_PPCompClosureI: public IA_ClosureI<P,T> {
    IA_Closure<P,Q> *f;
    IA_CoreImage<Q,T>	im;
  public:
    IA_PPCompClosureI( const IA_Closure< P,Q> &f_,
		       const IA_CoreImage<Q,T> &im_)
    :IA_ClosureI<P,T>(IA_PPCompClosurePS<P,Q>(f_, im_.domain())),
     f(f_.clone_self()), im(im_) { }
    IA_PPCompClosureI(const IA_PPCompClosureI &other) 
    :IA_ClosureI<P,T>(IA_PPCompClosurePS<P,Q>(*other.f, other.im.domain())),
     f(other.f->clone_self()), im(other.im) { }
    ~IA_PPCompClosureI() { delete f; }

    T operator()(const P&p) const {
	return im((*f)(p));
    }
    IA_ClosureI<P,T>* clone_self() const {
	return new IA_PPCompClosureI<P,Q,T>(*this);
    }
#if 0
    T *range(unsigned *n) const {
	return im.range(n);
    }
#endif
};


template <class P, class T>
IA_CoreImage<P,T> compose
(const IA_CoreImage<P,T>& im,
 const IA_Closure<P,P> &f)
{
    return IA_PPCompClosureI<P,P,T>(f, im);
}

//
//
//

#if 0

template <class P, class T>
IA_CoreImage<P,T> compose(T (*f)(T), const IA_CoreImage<P,T> &i)
{
    IA_IVIter<P,T>	iter(i);
    const int size = i.card();
    T * const vals = new T[size];

    {
	T	*scan=vals;
	T	temp;
	while (iter(temp))
	    *(scan++) = f(temp);
    }

    return IA_CoreImage<P,T>(i.domain(), vals, size, 1);
}

template <class P, class S, class T>
IA_CoreImage<P,S> compose(S (*f)(T), const IA_CoreImage<P,T> &i, const S*)
{
    IA_IVIter<P,T>	iter(i);
    const int size = i.card();
    S * const vals = new S[size];

    {
	S	*scan=vals;
	T	temp;
	while (iter(temp))
	    *(scan++) = f(temp);
    }

    return IA_CoreImage<P,S>(i.domain(), vals, size, 1);
}

template <class P, class R, class S, class T>
IA_CoreImage<P,R> compose(R (*f)(S), const IA_CoreImage<P,T> &i, const R*, const S*)
{
    IA_IVIter<P,T>	iter(i);
    const int size = i.card();
    R * const vals = new R[size];

    {
	R	*scan=vals;
	T	temp;
	while (iter(temp))
	    *(scan++) = f(temp);
    }

    return IA_CoreImage<P,R>(i.domain(), vals, size, 1);
}
#endif

template <class P, class Q, class R, class S, class T>
IA_CoreImage<P,Q> compose(R (*f)(S), const IA_CoreImage<P,T> &i, const Q*, const R*, const S*)
{
    IA_IVIter<P,T>	iter(i);
    const int size = i.card();
    Q * const vals = new Q[size];

    {
	Q	*scan=vals;
	T	temp;
	while (iter(temp))
	    *(scan++) = f(temp);
    }

    return IA_CoreImage<P,Q>(i.domain(), vals, size, 1);
}

template <class P, class Q, class R, class S, class T>
IA_CoreImage<P,Q> compose(const IA_Closure<S,R>&f,
			  const IA_CoreImage<P,T> &i,
			  const Q*)
{
    IA_IVIter<P,T>	iter(i);
    const int size = i.card();
    Q * const vals = new Q[size];

    {
	Q	*scan=vals;
	T	temp;
	while (iter(temp))
	    *(scan++) = f(temp);
    }

    return IA_CoreImage<P,Q>(i.domain(), vals, size, 1);
}

#if 0
template <class P, class R, class S, class T>
IA_CoreImage<P,R> compose(const IA_Closure<S,R>& f,
			  const IA_CoreImage<P,T> &i)
{
    IA_IVIter<P,T>	iter(i);
    const int size = i.card();
    R * const vals = new R[size];

    {
	R	*scan=vals;
	T	temp;
	while (iter(temp))
	    *(scan++) = f(temp);
    }

    return IA_CoreImage<P,R>(i.domain(), vals, size, 1);
}
#endif
