////////////////////////////////////////////////////////////////////////////
// lazy_reuse.hpp
//
// Build lazy operations for Phoenix equivalents for FC++
//
// These are equivalents of the Boost FC++ functoids in reuse.hpp
//
// Implemented so far:
//
// reuser1 (not yet tested)
// reuser2 (not yet tested)
// reuser3
//
// NOTE: It has been possible to simplify the operation of this code.
//       It now makes no use of boost::function or old FC++ code.
//
//       The functor type F must be an operator defined with
//       boost::phoenix::function.
//       See the example Apply in lazy_prelude.hpp
//
////////////////////////////////////////////////////////////////////////////
/*=============================================================================
    Copyright (c) 2000-2003 Brian McNamara and Yannis Smaragdakis
    Copyright (c) 2001-2007 Joel de Guzman
    Copyright (c) 2015 John Fletcher

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/

#ifndef BOOST_PHOENIX_FUNCTION_LAZY_REUSE
#define BOOST_PHOENIX_FUNCTION_LAZY_REUSE

#include <boost/phoenix/core.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/intrusive_ptr.hpp>


namespace boost {

  namespace phoenix {

    namespace fcpp {

//////////////////////////////////////////////////////////////////////
// Original FC++ comment:
// "Reuser"s are effectively special-purpose versions of curry() that
// enable recursive list functoids to reuse the thunk of the curried
// recursive call.  See
//    http://www.cc.gatech.edu/~yannis/fc++/New/reusers.html
// for a more detailed description.
//////////////////////////////////////////////////////////////////////

// For efficiency, we mark parameters as either "VAR"iant or "INV"ariant.
struct INV {};
struct VAR {};

template <class V, class X> struct Maybe_Var_Inv;
template <class X>
struct Maybe_Var_Inv<VAR,X> {
   static void remake( X& x, const X& val ) {
      x.~X();
      new (&x) X(val);
   }
   static X clone( const X& x ) { return X(x); }
};
template <class X>
struct Maybe_Var_Inv<INV,X> {
   static void remake( X&, const X& ) {}
   static const X& clone( const X& x ) { return x; }
};

/////////////////////////////////////////////////////////////////////
// ThunkImpl is an implementation of Fun0Impl for this use.
/////////////////////////////////////////////////////////////////////

template <class Result>
class ThunkImpl
{
   mutable RefCountType refC;
public:
   ThunkImpl() : refC(0) {}
   virtual Result operator()() const =0;
   virtual ~ThunkImpl() {}
   template <class X>
   friend void intrusive_ptr_add_ref( const ThunkImpl<X>* p );
   template <class X>
   friend void intrusive_ptr_release( const ThunkImpl<X>* p );
};

template <class T>
void intrusive_ptr_add_ref( const ThunkImpl<T>* p ) {
   ++ (p->refC);
}
template <class T>
void intrusive_ptr_release( const ThunkImpl<T>* p ) {
   if( !--(p->refC) ) delete p;
}

//////////////////////////////////////////////////////////////////////
// reuser1 is needed in list<T> operations
//////////////////////////////////////////////////////////////////////

template <class V1, class V2, class F, class X>
struct reuser1;

template <class V1, class V2, class F, class X, class R>
struct Thunk1 : public ThunkImpl<R> {
   mutable F f;
   mutable X x;
   Thunk1( const F& ff, const X& xx ) : f(ff), x(xx) {}
   void init( const F& ff, const X& xx ) const {
      Maybe_Var_Inv<V1,F>::remake( f, ff );
      Maybe_Var_Inv<V2,X>::remake( x, xx );
   }
   R operator()() const {
      return Maybe_Var_Inv<V1,F>::clone(f)(
         Maybe_Var_Inv<V2,X>::clone(x),
         reuser1<V1,V2,F,X>(this) );
   }
};

template <class V1, class V2, class F, class X>
struct reuser1 {
   typedef typename F::template result<F(X)>::type R;
   typedef typename boost::phoenix::function<R> fun0_type;
   typedef Thunk1<V1,V2,F,X,R> M;
   typedef M result_type;
   boost::intrusive_ptr<const M> ref;
   reuser1(a_unique_type_for_nil) {}
   reuser1(const M* m) : ref(m) {}
   M operator()( const F& f, const X& x ) {
      if( !ref )   ref = boost::intrusive_ptr<const M>( new M(f,x) );
      else         ref->init(f,x);
      return *ref;
   }
   void iter( const F& f, const X& x ) {
      if( ref )    ref->init(f,x);
   }
};

//////////////////////////////////////////////////////////////////////
// reuser2 is needed in list<T>
//////////////////////////////////////////////////////////////////////

template <class V1, class V2, class V3, class F, class X, class Y>
struct reuser2;

template <class V1, class V2, class V3, class F, class X, class Y, class R>
struct Thunk2 : public ThunkImpl<R> {
   mutable F f;
   mutable X x;
   mutable Y y;
   Thunk2( const F& ff, const X& xx, const Y& yy ) : f(ff), x(xx), y(yy) {}
   void init( const F& ff, const X& xx, const Y& yy ) const {
      Maybe_Var_Inv<V1,F>::remake( f, ff );
      Maybe_Var_Inv<V2,X>::remake( x, xx );
      Maybe_Var_Inv<V3,Y>::remake( y, yy );
   }
   R operator()() const {
      return Maybe_Var_Inv<V1,F>::clone(f)(
         Maybe_Var_Inv<V2,X>::clone(x),
         Maybe_Var_Inv<V3,Y>::clone(y),
         reuser2<V1,V2,V3,F,X,Y>(this) );
   }
};

template <class V1, class V2, class V3, class F, class X, class Y>
struct reuser2 {
   typedef typename F::template result<F(X,Y)>::type R;
   typedef Thunk2<V1,V2,V3,F,X,Y,R> M;
   typedef M result_type;
   boost::intrusive_ptr<const M> ref;
   reuser2(a_unique_type_for_nil) {}
   reuser2(const M* m) : ref(m) {}
   M operator()( const F& f, const X& x, const Y& y ) {
      if( !ref )   ref = boost::intrusive_ptr<const M>( new M(f,x,y) );
      else         ref->init(f,x,y);
      return *ref;
   }
   void iter( const F& f, const X& x, const Y& y ) {
      if( ref )    ref->init(f,x,y);
   }
};

//////////////////////////////////////////////////////////////////////
// reuser3
//////////////////////////////////////////////////////////////////////

template <class V1, class V2, class V3, class V4,
          class F, class X, class Y, class Z>
struct reuser3;

template <class V1, class V2, class V3, class V4,
          class F, class X, class Y, class Z, class R>
struct Thunk3 : public ThunkImpl<R> {
   mutable F f;
   mutable X x;
   mutable Y y;
   mutable Z z;
   Thunk3( const F& ff, const X& xx, const Y& yy, const Z& zz )
      : f(ff), x(xx), y(yy), z(zz) {}
   void init( const F& ff, const X& xx, const Y& yy, const Z& zz ) const {
      Maybe_Var_Inv<V1,F>::remake( f, ff );
      Maybe_Var_Inv<V2,X>::remake( x, xx );
      Maybe_Var_Inv<V3,Y>::remake( y, yy );
      Maybe_Var_Inv<V4,Z>::remake( z, zz );
   }
   R operator()() const {
      return Maybe_Var_Inv<V1,F>::clone(f)(
         Maybe_Var_Inv<V2,X>::clone(x),
         Maybe_Var_Inv<V3,Y>::clone(y),
         Maybe_Var_Inv<V4,Z>::clone(z),
         reuser3<V1,V2,V3,V4,F,X,Y,Z>(this) );
   }
};

template <class V1, class V2, class V3, class V4,
          class F, class X, class Y, class Z>
struct reuser3 {
   typedef typename F::template result<F(X,Y,Z)>::type R;
   typedef Thunk3<V1,V2,V3,V4,F,X,Y,Z,R> M;
   typedef M result_type;
   boost::intrusive_ptr<const M> ref;
   reuser3(a_unique_type_for_nil) {}
   reuser3(const M* m) : ref(m) {}
   M operator()( const F& f, const X& x, const Y& y, const Z& z ) {
      if( !ref )   ref = boost::intrusive_ptr<const M>( new M(f,x,y,z) );
      else         ref->init(f,x,y,z);
      return *ref;
   }
   void iter( const F& f, const X& x, const Y& y, const Z& z ) {
      if( ref )    ref->init(f,x,y,z);
   }
};

    }

  }
}

#endif
