/*=======================================================================*\
 * example.cc : examples for bimonotone enumeration streams
 * Copyright (C) 2003-2005 Michael Eisermann, all rights reserved
 * Institut Fourier, Université Grenoble I, France
 *
 * Please send bug reports, suggestions, and comments to
 *   Michael Eisermann <Michael.Eisermann@ujf-grenoble.fr>
 * For more information and updates see the web page
 *   http://www-fourier.ujf-grenoble.fr/~eiserm
 * If you have used my software in your research, please let me know.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version. In particular, permission to use, 
 * copy, modify, or redistribute this software for research and education 
 * is granted without fee, provided that the above copyright notice and 
 * this permission appear in all copies and supporting documentation. 
 *
 * This software is distributed in the hope that it will be useful,
 * but it is provided "as is" without any warranty, without even 
 * the implied warranty of fitness for a particular purpose. 
 * See the GNU General Public License for more details.
 *
 *-----------------------------------------------------------------------
 * Description:
 *-------------
 * This program shows some sample applications of bimonotone enumeration.
 * Its only purpose is to illustrate the stream templates implemented 
 * in the file "bimonotone.hh".
\*=======================================================================*/

// Include standard libraries
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;

// Include templates for sorted enumeration
#include "bimonotone.hh"

/*-----------------------------------------------------------------------*\
 * We first define the function f(a,b) = a^2 + b^2 for a >= 0 and b >= 0.
 *-----------------------------------------------------------------------
 * In order to comply with the template requirements in "bimonotone.hh"
 * this will be a function object whose class is derived from BimABtoZ.
 * For the algorithms to work correctly, we require that f : X -> Z be 
 * a proper bimonotone function defined on a bimonotone domain X.
 * (See the comments for the base class BimABtoZ in "bimonotone.hh".)
 * These conditions are satisfied by the implementations that follow.
\*-----------------------------------------------------------------------*/
class Function1 : public BimABtoZ<long,long,long,short>
{
public:
  // Descriptive text for this function
  string description() const
    { return string("f(a,b) = a^2 + b^2, with a>=0 and b>=0"); };
  
  // Typenames inherited from BimABtoZ
  typedef BimABtoZ<int,int,int>::A A;
  typedef BimABtoZ<int,int,int>::B B;
  typedef BimABtoZ<int,int,int>::Z Z;
  typedef BimABtoZ<int,int,int>::D D;
  
  // Evaluation of the function f, here f(a,b) = a^2 + b^2.
  Z operator() ( A a, B b ) const 
    { return ( a*a + b*b ); };
  
  // Define the domain X of A x B where the function is defined.
  A a_min() const { return A(0); };
  B b_min() const { return B(0); };
  bool domain_contains( A a, B b ) const 
    { return ( a>=0 && b>=0 ); };
  
  // First differential f(a+1,b) = f(a,b) + diff1(a,b)
  // Here diff1(a,b) = ((a+1)^2+b^2) - (a^2+b^2) = 2a+1
  D diff1( A a, B b ) const
    { return 2*a+1; };
  
  // Second differential f(a,b+1) = f(a,b) + diff2(a,b)
  // Here diff2(a,b) = (a^2+(b+1)^2) - (a^2+b^2) = 2b+1
  D diff2( A a, B b ) const
    { return 2*b+1; };
};

/*-----------------------------------------------------------------------*\
 * We then define the restriction to 0<=a<100 and 0<=b<100.
\*-----------------------------------------------------------------------*/
class Function2 : public Function1
{
public:
  // Descriptive text for this function
  string description() const
    { return string("f(a,b) = a^2 + b^2, with 0<=a<100 and 0<=b<100"); };

  // Define the domain X of A x B where the function is defined.
  bool domain_contains( A a, B b ) const 
    { return ( a>=0 && a<100 && b>=0 && b<100 ); };
};

/*-----------------------------------------------------------------------*\
 * We can also define the restriction to 0 <= a <= b <= 100
\*-----------------------------------------------------------------------*/
class Function3 : public Function1
{
public:
  // Descriptive text for this function
  string description() const
    { return string("f(a,b) = a^2 + b^2, with 0 <= a <= b <= 100"); };

  // Define the domain X of A x B where the function is defined.
  bool domain_contains( A a, B b ) const 
    { return ( 0<=a && a<=b && b<=100 ); };
};

/*-----------------------------------------------------------------------*\
 * We finally define a more complicated domain. 
 * The only requirement is that it be bimonotone.
 * (See the comments for the base class BimABtoZ in "bimonotone.hh".)
\*-----------------------------------------------------------------------*/
class Function4 : public Function1
{
public:
  // Descriptive text for this function
  string description() const
    { return string("f(a,b) = a^2 + b^2, with a <= b and b^2 <= 1+10a"); };

  // Define the domain X of A x B where the function is defined.
  bool domain_contains( A a, B b ) const 
    { return ( a <= b  &&  b*b <= 1+10*a ); };
};

/*-----------------------------------------------------------------------*\
 * Produce values in increasing order
\*-----------------------------------------------------------------------*/
template <typename A, typename B, typename Z>
void produce_values( BinStream<A,B,Z>& theStream )
{
  for(;;)
    {
      cout << "\nCurrent status: " 
	   << " length=" << theStream.length()
	   << " width=" << theStream.width();
      if ( theStream.empty() )
	cout << " (The stream is empty.)" << endl;
      else
	cout << " level=" << theStream.value() << endl;
      
      char answer= '?';
      while( answer!='r' && answer!='e' && answer!='q' ) 
	{
	  cout << "What would you like to do? (e/r/q) ";
	  cin >> answer;
	};
      
      if ( answer == 'q' )
	{
	  cout << "Ok, so let's stop here." << endl;
	  break;
	};
      
      if ( answer == 'e' )
	{
	  // cout << "How many values do you wish to enumerate ? ";
	  int number;
	  cin >> number;
	  if ( number < 1 ) continue;
	  
	  cout << "enumerate " << number << " values..." << endl;
	  for( int i=0; i<number; ++i )
	    {
	      if ( theStream.empty() ) 
		{
		  cout << "(The stream is empty.)";
		  break;
		}
	      A a= theStream.first();
	      B b= theStream.second();
	      Z z= theStream.value();
	      cout << "(" << a << "," << b << ") -> " << z << "   ";
	      theStream.advance();
	    }
	  cout << endl;
	  continue;
	}

      if ( answer == 'r' )
	{
	  // cout << "To which level would you like to reset the stream ? ";
	  Z level;
	  cin >> level;
	  cout << "reset to level " << level << "..." << endl;
	  theStream.reset( level );
	  continue;
	};
    };
};

/*-----------------------------------------------------------------------*\
 * Some sample applications of bimonotone enumeration
\*-----------------------------------------------------------------------*/
int main()
{ 
  cout << "\nThis program shows some sample applications of bimonotone "
       << "enumeration;\nits only purpose is to illustrate the use of "
       << "enumeration stream templates.\nIn our example, we consider "
       << "sorted enumeration for f(a,b) = a^2 + b^2.\n" << endl;

  cout << "The following enumerations specialize the function f to different\n"
       << "domains of defintion. You can type\n"
       << "e <n>  to enumerate <n> values,\n"
       << "r <l>  to reset to level <l>,\n"
       << "q      to quit." << endl;
    
  cout << "\n\n\n" << Function1().description() << endl;
  BimStream< Function1 > stream1;
  produce_values( stream1 );

  cout << "\n\n\n" << Function1().description() << endl;
  cout << "(differential model)" << endl;
  BimStreamDiff< Function1 > stream1diff( 100 );
  produce_values( stream1diff );

  cout << "\n\n\n" << Function2().description() << endl;
  BimStream< Function2 > stream2;
  produce_values( stream2 );

  cout << "\n\n\n" << Function2().description() << endl;
  cout << "(differential model)" << endl;
  BimStreamDiff< Function2 > stream2diff( 100 );
  produce_values( stream2diff );

  cout << "\n\n\n" << Function3().description() << endl;
  BimStream< Function3 > stream3;
  produce_values( stream3 );

  cout << "\n\n\n" << Function3().description() << endl;
  cout << "(differential model)" << endl;
  BimStreamDiff< Function3 > stream3diff( 100 );
  produce_values( stream3diff );

  cout << "\n\n\n" << Function4().description() << endl;
  BimStream< Function4 > stream4;
  produce_values( stream4 );

  cout << "\n\n\n" << Function4().description() << endl;
  cout << "(differential model)" << endl;
  BimStreamDiff< Function4 > stream4diff( 100 );
  produce_values( stream4diff );

  cout << "\nThat's all. Bye.\n" << endl;
};

/*=======================================================================*\
 * end of file "example.cc"
\*=======================================================================*/

