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

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


// 
// $Log: LazyPS.h,v $
// Revision 1.14  1994/07/25  16:58:52  thoth
// \Name sanitization
//
// Revision 1.13  1993/11/17  18:06:09  thoth
// extensivep() is now extensive()
//
// Revision 1.12  1993/05/28  16:13:39  thoth
// fix strange characters.
//
// Revision 1.11  1993/05/26  16:51:32  thoth
// Copyright Notices
//
// Revision 1.10  93/04/19  23:27:44  thoth
// Convert BaseIPS to IA_BasePS<IA_IntPoint>
// 
// Revision 1.9  93/04/17  15:19:34  thoth
// IA_IntPoint upgrades (dim, inf, sup)
// 
// Revision 1.8  93/02/12  16:25:30  thoth
// more IA_ prefixes.
// more comments.
// 
// Revision 1.7  93/01/20  11:43:11  thoth
// we don't need that header.
// 
// Revision 1.6  92/12/16  14:48:14  thoth
// conversion to IA_ mostly complete
// 
// Revision 1.5  92/09/30  10:37:56  thoth
// Lazies are now templatized.
// contains now accepts const Point<>&.
// type() system now based on address of static data member.
// 
// Revision 1.4  92/09/20  17:41:19  thoth
// many members are now const.
// 
// Revision 1.3  92/09/20  14:07:07  thoth
// LazyIPS::contains now returns 0 or 1 guaranteed
// 
// Revision 1.2  92/09/14  21:54:31  thoth
// fixed dyslexia.
// added commutativity of all binary operations except /
// 
// Revision 1.1  92/09/03  13:37:11  thoth
// Initial revision
// 
//

#ifndef LazyPS_h_
#define LazyPS_h_

// This file contains the implementation of most comprehensive pointsets.
// A comprehensive pointset is a pointset that we can not iterate over
// (because we can not guarantee that the set is finite).  The only simple
// operation you can perform on a comprehensive pointset is to ask whether
// a point is a member or not.

#ifdef IA_PRII
#pragma interface
#endif

#include "BasePS.h"
#include "Set.h"

// This implementation is used by the IntPointSet container when
// the programmer provides us with a predicate function that tells us
// if a point is contained in the pointset or not.
template <class P>
class IA_PredicatePS_V: public IA_BasePS<P> {
private:
    static char type_;
protected:
    int (*pred)(P);
public:
    IA_PredicatePS_V(unsigned dim, int(*f)(P)) :IA_BasePS<P>(dim) {pred=f;}

    int equal(IA_BasePS<P>*other) const {
	return other->type()==this->type() &&
	    pred == ((IA_PredicatePS_V<P>*)other)->pred;
    }
    unsigned hash_self(unsigned mod) const {
	return ((unsigned long)pred)%mod;
    }
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains (const P &ip) const { return 0!=(*pred)(ip); }
    int extensive() const { return 0; }
    void output(ostream&, unsigned=0) const;
};

// Same as above, but the function takes a slightly different argument
// (the calling syntax is identical though).
template <class P>
class IA_PredicatePS_CR: public IA_BasePS<P> {
private:
    static char type_;
protected:
    int (*pred)(const P &);
public:
    IA_PredicatePS_CR(unsigned dim, int(*f)(const P &)) :IA_BasePS<P>(dim) {pred=f;}

    int equal(IA_BasePS<P>*other) const {
	return other->type()==this->type() &&
	    pred == ((IA_PredicatePS_CR<P>*)other)->pred;
    }
    unsigned hash_self(unsigned mod) const {
	return ((unsigned long)pred)%mod;
    }
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains (const P &ip) const { return 0!=(*pred)(ip); }
    int extensive() const { return 0; }
    void output(ostream&, unsigned=0) const;
};

//
//
//

//  The following comprehensive pointsets are formed by non-simple
// operations on comprehensive pointsets.  They are termed Lazy because
// we can not compute an extensive result and we don\'t compute
// containedness unless the user asks us.

template <class P_t>
class IA_LazyBinaryPS: public IA_BasePS<P_t> {
    // this class is still abstract.  It just seemed like a good place to
    // express the commonality of all the binary PSs.
protected:
    const IA_Set<P_t>	lhs,rhs;
public:
    IA_LazyBinaryPS(IA_Set<P_t> lhs_, IA_Set<P_t> rhs_)
	:lhs(lhs_), rhs(rhs_), IA_BasePS<P_t>(rhs_.dim()) {
	    if (rhs.dim() != lhs.dim()) {
		ia_throw(Pointset_DimensionMismatch_Exception(__FILE__, __LINE__));
	    }
	}

    // These are appropriate for most binary LazyPSs.
    // Any PS it isn't appropriate for can override them
    int equal(IA_BasePS<P_t>* other) const {
	IA_LazyBinaryPS *ot = (IA_LazyBinaryPS*)other;
	return this->type() == other->type() &&
	    (rhs==ot->rhs && lhs==ot->lhs || // commutativity
	     rhs==ot->lhs && lhs==ot->rhs);
    }
    unsigned hash_self(unsigned mod) const {
	return (rhs.hash_self(mod) + lhs.hash_self(mod) +
		(int)this->type() ) % mod;
    }

    int extensive() const { return 0; }

    // the following virtual functions are still abstract:
    // type, contains, output
};

// The union of a comprehensive pointset with any sort of pointset
template <class P_t>
class IA_LazyUnionPS: public IA_LazyBinaryPS<P_t> {
private:
    static char type_;
public:
    IA_LazyUnionPS(IA_Set<P_t> l, IA_Set<P_t> r) :IA_LazyBinaryPS<P_t>(l,r) {}
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P_t& ip) const {
	return rhs.contains(ip) || lhs.contains(ip);
    }
    void output(ostream&, unsigned=0) const;
};

// The intersection of two comprehensive pointsets (if one were extensive
// we could compute an extensive result industriously).
template <class P_t>
class IA_LazyIntersectionPS: public IA_LazyBinaryPS<P_t> {
private:
    static char type_;
public:
    IA_LazyIntersectionPS(IA_Set<P_t> l, IA_Set<P_t> r) :IA_LazyBinaryPS<P_t>(l,r) {}
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P_t &ip) const {
	return rhs.contains(ip) && lhs.contains(ip);
    }
    void output(ostream&, unsigned=0) const;
};

// The symmetric difference of a comprehensive pointset with any sort
// of pointset
template <class P_t>
class IA_LazyXORPS: public IA_LazyBinaryPS<P_t> {
private:
    static char type_;
public:
    IA_LazyXORPS(IA_Set<P_t> l, IA_Set<P_t> r) :IA_LazyBinaryPS<P_t>(l,r) {}
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P_t &ip) const {
	return (0==rhs.contains(ip)) == (0!=lhs.contains(ip));
    }
    void output(ostream&, unsigned=0) const;
};

// The asymmetric difference between a comprehensive pointset and any
// sort of pointset (if the first argument were extensive, we could
// compute an extensive result industriously).
template <class P_t>
class IA_LazyMinusPS: public IA_LazyBinaryPS<P_t> {
private:
    static char type_;
public:
    IA_LazyMinusPS(IA_Set<P_t> l, IA_Set<P_t> r) :IA_LazyBinaryPS<P_t>(l,r) {}
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    // Minus is not commutative, override equal method
    int equal(IA_BasePS<P_t>* other) const {
	IA_LazyMinusPS *ot = (IA_LazyMinusPS*)other;
	return this->type() == other->type() &&
	    (rhs==ot->rhs && lhs==ot->lhs);
    }
    int contains(const P_t &ip) const {
	return lhs.contains(ip) && !rhs.contains(ip);
    }
    void output(ostream&, unsigned=0) const;
};

// The minkowsky addition of an extensive pointset with a comprehensive
// pointset (two extensives can be computed industriously, two
// comprehensives are uncomputable).
template <class P_t>
class IA_LazyMinkowskiPS: public IA_LazyBinaryPS<P_t> {
private:
    static char type_;
    // rhs is the comprehensive and lhs is the extensive
    IA_Set<P_t> comp() const { return rhs; }
    IA_Set<P_t> ext() const { return lhs; }
public:
    IA_LazyMinkowskiPS(IA_Set<P_t> l, IA_Set<P_t> r) :IA_LazyBinaryPS<P_t>(l,r) {}
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P_t &ip) const;
    void output(ostream&, unsigned=0) const;
};

template <class P>
class IA_LazyConcatenation: public IA_LazyBinaryPS<P> {
private:
    static char type_;
public:
    IA_LazyConcatenation(IA_Set<P> l, IA_Set<P> r)
	: IA_LazyBinaryPS<P>(l, r) { }
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P &ip) const {
	return lhs.contains(cdr(ip, -rhs.dim()))
	    && rhs.contains(cdr(ip, lhs.dim()));
    }
    void output(ostream&, unsigned=0) const;
};

//
//

template <class P_t>
class IA_LazyUnaryPS: public IA_BasePS<P_t> {
    // this class is still abstract.  It just seemed like a good place to
    // express the commonality of all the unary PSs.
protected:
    const IA_Set<P_t>	arg;
public:
    IA_LazyUnaryPS(IA_Set<P_t> PS)
	:arg(PS), IA_BasePS<P_t>(PS.dim()) {}

    // these are appropriate for most unary LazyPSs
    // any PS it isn't appropriate for can override them
    int equal(IA_BasePS<P_t>* other) const {
	IA_LazyUnaryPS *ot = (IA_LazyUnaryPS*)other;
	return this->type() == other->type() &&
	    arg==ot->arg;
    }
    unsigned hash_self(unsigned mod) const {
	return (arg.hash_self(mod) + (int)this->type() ) % mod;
    }

    int extensive() const { return 0; }

    // the following virtual functions are still abstract:
    // type, contains, output
};

//
//

template <class P_t>
class IA_LazyUnaryBPS: public IA_BasePS<P_t> {
    // this class is still abstract.  It just seemed like a good place to
    // express the commonality of all the unary PSs.
protected:
    const IA_BasePS<P_t>	*arg;
public:
    IA_LazyUnaryBPS(const IA_BasePS<P_t> *PS)
	:arg(PS), IA_BasePS<P_t>(PS->dim()) {
	    // cast away const
	    ((IA_BasePS<P_t>*)arg)->enref();
	}

    ~IA_LazyUnaryBPS() {
	// cast away const
	hash_managed_delete((IA_BasePS<P_t>*)arg);
    }
    // these are appropriate for most unary LazyPSs
    // any PS it isn't appropriate for can override them
    int equal(IA_BasePS<P_t>* other) const {
	IA_LazyUnaryBPS *ot = (IA_LazyUnaryBPS*)other;
	return this->type() == other->type() &&
	    arg==ot->arg;
    }
    unsigned hash_self(unsigned mod) const {
	return (arg->hash_self(mod) + (int)this->type() ) % mod;
    }

    int extensive() const { return 0; }

    // the following virtual functions are still abstract:
    // type, contains, output
};

// The inversion of the set membership predicate.  The argument can be
// any kind of pointset.
template <class P_t>
class IA_LazyNotPS: public IA_LazyUnaryPS<P_t> {
private:
    static char type_;
public:
    IA_LazyNotPS(IA_Set<P_t> x) :IA_LazyUnaryPS<P_t>(x) {}
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P_t &ip) const { return ! arg.contains(ip); }
    void output(ostream&, unsigned=0) const;
};

// This is really a binary operation, but it only has one pointset operand.
// It is the offsetted version of a comprehensive pointset.
template <class P_t>
class IA_LazyOffsettedPS: public IA_LazyUnaryPS<P_t> {
private:
    static char type_;
protected:
    const P_t	offset;
public:
    IA_LazyOffsettedPS(IA_Set<P_t> x, P_t off):IA_LazyUnaryPS<P_t>(x),offset(off) {
	if (x.dim() != off.dim()) {
	    ia_throw(Pointset_DimensionMismatch_Exception(__FILE__, __LINE__));
	}
    }
    int equal(IA_BasePS<P_t>*other)const {
	return IA_LazyUnaryPS<P_t>::equal(other) &&
	    offset == ((IA_LazyOffsettedPS*)other)->offset;
    }
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P_t &ip) const { return arg.contains(ip-offset); }
    void output(ostream&, unsigned=0) const;
};

template <class P>
class IA_LazyOriginReflectionPS: public IA_LazyUnaryBPS<P> {
private:
    static char type_;
public:
    IA_LazyOriginReflectionPS(const IA_BasePS<P> *x)
	:IA_LazyUnaryBPS<P>(x) { }
    // LazyUnary equal method is just fine
    static IA::Type s_type() { return &type_; }
    IA::Type type() const { return &type_; }
    int contains(const P &ip) const { return arg->contains(-ip); }
    void output(ostream&, unsigned=0) const;
};

#endif
