#ifndef PARTITIONEQU_H
#define PARTITIONEQU_H

#include <typedefs.h>
#include <partitionspace.h>



template< typename P, typename Q >
class partitionequmult;

template< typename Q >
class partitionequnot;

/*!
\brief And operator.
*/
template< typename P, typename Q >
class partitionequadd 
{
public:

  /** Left argument of binary operator. */
  P left;
  /** Right argument of binary operator. */
  Q right;

  /** Construct the binary operator from left and right expressions. */
  partitionequadd
  ( 
    P const & _left,
    Q const & _right
  )
    : left(_left), right(_right) {}

  /** Intersect the partitions. */
  template< typename W >
  partitionequmult< partitionequadd<P,Q>, W > operator * ( W const & x )
    { return partitionequmult< partitionequadd<P,Q>, W >(*this,x); }

  /** Join the partitions. */
  template< typename W >
  partitionequadd< partitionequadd<P,Q>, W > operator + ( W const & x )
    { return partitionequadd< partitionequadd<P,Q>, W >(*this,x); }

  /** Logically negate the partition. */
  partitionequnot< partitionequadd<P,Q> > operator ! ()
    { return partitionequnot< partitionequadd<P,Q> >(*this); }

  /** Subtract partition x from this partition. */
  template< typename W >
  partitionequmult< partitionequadd<P,Q>, partitionequnot<W> > operator - ( W const & x )
    { return partitionequmult< partitionequadd<P,Q>, partitionequnot<W> >(*this,partitionequnot<W>(x)); }

  /** Union/and the partition spaces. */
  template< typename T >
  boolc isInside( T const & x ) const
  {
    if (left.isInside(x)==true)
      return true;

    return right.isInside(x);
  }

};


/*!
\brief Multipication operator.
*/
template< typename P, typename Q >
class partitionequmult 
{
public:

  /** Left argument of binary operator. */
  P left;
  /** Right argument of binary operator. */
  Q right;

  /** Construct the binary operator from left and right expressions. */
  partitionequmult
  ( 
    P const & _left,
    Q const & _right
  )
    : left(_left), right(_right) {}

  /** Intersect the partitions. */
  template< typename W >
  partitionequmult< partitionequmult<P,Q>, W > operator * ( W const & x )
    { return partitionequmult< partitionequmult<P,Q>, W >(*this,x); }

  /** Join the partitions. */
  template< typename W >
  partitionequadd< partitionequmult<P,Q>, W > operator + ( W const & x )
    { return partitionequadd< partitionequmult<P,Q>, W >(*this,x); }

  /** Logically negate the partition. */
  partitionequnot< partitionequmult<P,Q> > operator ! ()
    { return partitionequnot< partitionequmult<P,Q> >(*this); }

  /** Subtract partition x from this partition. */
  template< typename W >
  partitionequmult< partitionequmult<P,Q>, partitionequnot<W> > operator - ( W const & x )
    { return partitionequmult< partitionequmult<P,Q>, partitionequnot<W> >(*this,partitionequnot<W>(x)); }

  /** Intersect/multiply the partition spaces. */
  template< typename T >
  boolc isInside( T const & x ) const
  {
    if (left.isInside(x)==false)
      return false;

    return right.isInside(x);
  }

};

/*!
\brief Logical not operator.
*/
template< typename Q >
class partitionequnot
{
public:
  /** Right hand side of the operator. */
  Q right;
 
  /** Unary operator. */
  partitionequnot( Q const & _right )
    : right(_right) {}

  /** Join the partitions. */
  template< typename W >
  partitionequadd< partitionequnot<Q>, W > operator + ( W const & x )
    { return partitionequadd< partitionequnot<Q>, W >(*this,x); }

  /** Intersect the partitions. */
  template< typename W >
  partitionequmult< partitionequnot<Q>, W > operator * ( W const & x )
    { return partitionequmult< partitionequnot<Q>, W >(*this,x); }

  /** Logically negate the partition. */
  partitionequnot< partitionequnot<Q> > operator ! ()
    { return partitionequnot< partitionequnot<Q> >(*this); }

  /** Subtract partition x from this partition. */
  template< typename W >
  partitionequmult< partitionequnot<Q>, partitionequnot<W> > operator - ( W const & x )
    { return partitionequmult< partitionequnot<Q>, partitionequnot<W> >(*this,partitionequnot<W>(x)); }

  /** Logically not the partition spaces. */
  template< typename T >
  boolc isInside( T const & x ) const
    { return ! right.isInside(x); } 

};



/*
\brief Provide a base type and forward equation compilation to other classes.

Use Peq to convert to partitiondata.

\par Example
\verbatim
  d2halfspace< pt2, double > L[4];

  L[0].set( pt2(0.4,0.8), pt2(0.3,0.3) );
  L[1].set( pt2(0.3,0.3), pt2(0.7,0.2) );
  L[2].set( pt2(0.7,0.2), pt2(0.4,0.5) );
  L[3].set( pt2(0.4,0.5), pt2(0.4,0.8) );

  PeqCapture(ps,Peq(L[0])*Peq(L[1])*(Peq(L[2])+Peq(L[3])));
\endverbatim
*/
template< typename T >
class partitiondata
{
public:

  /** The client ensures that this reference is alive. */
  partitionspace<T> * data;

  /** Forwards the test to the data. */
  boolc isInside( T const & x ) const
    { return data->isInside(x); }

  /** Join the partitions. */
  template< typename W >
  partitionequadd< partitiondata<T>, W > operator + ( W const & x )
    { return partitionequadd< partitiondata<T>, W >(*this,x); }

  /** Intersect the partitions. */
  template< typename W >
  partitionequmult< partitiondata<T>, W > operator * ( W const & x )
    { return partitionequmult< partitiondata<T>, W >(*this,x); }

  /** Logically not the partition. */
  partitionequnot< partitiondata<T> > operator ! ()
    { return partitionequnot< partitiondata<T> >(*this); }

  /** Subtract partition x from this partition. */
  template< typename W >
  partitionequmult< partitiondata<T>, partitionequnot<W> > operator - ( W const & x )
    { return partitionequmult< partitiondata<T>, partitionequnot<W> >(*this,partitionequnot<W>(x)); }

  /** This class does not manage the reference, it must be kept alive. */
  partitiondata(partitionspace<T> * _data)
    : data(_data) {}
};

/** Convert the partitionto partitiondata. */
template< typename T >
partitiondata<T> Peq(partitionspace<T> & p)
  { return partitiondata<T>(&p); }


/*!
\brief Hide the FN type by accessing this class through
       a partitionspace<T>* pointer. */
template< typename FN, typename T >
class partitionF : public partitionspace<T>
{
  /** Compiled function. */
  FN fn;
public:

  /** Bitwise copy of the compiled function. */
  partitionF(FN _fn) 
    : fn(_fn) {}

  /** Forward the work to the compiled function. */
  boolc isInside(T const & w) const
    { return fn.isInside(w); }
};

/** Capture a compiled function. */
template< typename FN, typename T >
void PeqCapture( partitionspace<T>* & p, FN const & fn )
  { p = new partitionF<FN,T>(fn); }




#endif



