#include <iostream>
#include <functional>

#include "misc.h"
#include "point.h"
#include "rand.h"

using namespace std;

void random(point3<double>& p)
{
   // p in [-1.0,1.0]

   f_rand_double r;

   p.x = r();
   p.y = r();
   p.z = r();
   p *= 2.0;
   p += (-1.0);
}

// conditional object

class nonpositive
{
public:
   bool operator () (const point3<double>& p)
   {
      return p.x<0.0 || p.y<0 || p.z<0.0;
   }
};

// conditional object, using my macro
func(nonpos,p.x<0.0 || p.y<0 || p.z<0.0,p);

void test01()
{
   //create random points in box at origin,

   const unsigned int n=30;
   vector< point3<double> > v(n);
   for (unsigned int i=0; i<n; ++i)
      random(v[i]);

   cout << printvecwrap< point3<double> >(2,v) << endl;

   cout << endl;
   cout << "making sure conditinal object works" << endl;
   point3<double> a01(.1,.2,.44), a02(.1,-.2,.44);
   cout << nonpositive()(a01) << endl; 
   cout << nonpositive()(a02) << endl;

   nonpos< bool, bool&, const point3<double>& > postest;
   cout << postest(a01) << endl;
   cout << postest(a02) << endl;

   //
   //  remove_if  doesn't alter size of container
   //  takes a conditional object and "removes" if condition is true
   //

   
   vector< point3<double> >::iterator new_end = remove_if(v.begin(),v.end(),postest);
   cout << "remove elements at end of the container" << endl;
   cout << printvecwrap< point3<double> >(2,v) << endl;
   
   cout << "v.erase(new_end,v.end());" << endl;
   v.erase(new_end,v.end());
   cout << printvecwrap< point3<double> >(2,v) << endl;
}

void test02()
{
   // Same as test01 without commentries

   const unsigned int n=30;
   vector< point3<double> > v(n);
   for (unsigned int i=0; i<n; ++i)
      random(v[i]);

   nonpos< bool, bool&, const point3<double>& > testnonpos;
   vector< point3<double> >::iterator new_end = remove_if(v.begin(),v.end(),testnonpos);
   v.erase(new_end,v.end());
   cout << printvecwrap< point3<double> >(2,v) << endl;
}

func(dist02,sqrt(p.x*p.x+p.y*p.y+p.z*p.z),p);

class compare1
{
   dist02<double,double&, const point3<double>& > f,g; 
public:
   bool operator()( const point3<double>& a, const point3<double>& b) 
   {
/*
// Compiler problem : f(a) > f(b) not evaluated properly
// solution: make explicit
      cout << SHOW(a) << endl;
      cout << SHOW(b) << endl;
      cout << SHOW( f(a) ) << endl;
      cout << SHOW( f(b) ) << endl;
      cout << SHOW( f(a) > f(b) ) << endl;
      cout << SHOW( (f(a)) > (f(b)) ) << endl;
      cout << SHOW( (f(a) > f(b)) ) << endl;
      double ta = f(a), tb = f(b);
      cout << SHOW (ta > tb) << endl;
      return f(a)>f(b);
*/
      //double ta = f(a), tb = f(b);
      //return ta > tb;
//  CLASSIC gotcha
//  f(a)>f(b) since the function stores the result, both these
//  are refering to the same location
      return f(a)>g(b);
   }
};
      
void test04()
{
   point3<double> p1(.2,.5,.7),p2(0,0,0),p3(1,3,5);
   dist02<double,double&, const point3<double>& > f;

   cout << SHOW( f(p1) ) << endl;
   cout << SHOW( f(p2) ) << endl;
   cout << SHOW( f(p3) ) << endl;

   compare1 c;
   cout << endl;
   cout << SHOW( c(p1,p2) ) << endl;
   cout << SHOW( c(p1,p3) ) << endl;
   cout << SHOW( c(p2,p1) ) << endl;
   cout << SHOW( c(p2,p3) ) << endl;
   cout << SHOW( c(p3,p1) ) << endl;
   cout << SHOW( c(p3,p2) ) << endl;
}


   
void test03()
{
   // sorting a vector

   // creating a vector with random points
   const unsigned int n=10;
   vector< point3<double> > v(n);
   for (unsigned int i=0; i<n; ++i)
      random(v[i]);

   cout << "before sort" << endl;
   cout << printvecwrap<point3<double> >(2,v) << endl << endl;

   sort(v.begin(),v.end(),compare1());

   // Displaying the result
   cout << endl;
   cout << printvecwrap<point3<double> >(2,v) << endl;
   dist02<double,double&, const point3<double>& > f;
   vector<double> vd;
   mapping(vd,v,f);
   cout << printvecwrap<double>(5,vd) << endl;

}

//
// compare applies a function to the data before comparison
//
// C is the comparison function
// F is the function applied to the data
// X is the data

// Example
//    compare<
//    less<double>,
//    dist02<double,double&, const point3<double>& >,
//    point3<double> > c;
//    ... c( point3<double>(1,2,5), point3<double>(.2,20,3) );

/*
template<class C, class F, class X>
class compare
{
   C c;
   F f,g;  // since functions store result, 2 are needed for comparison
public:
   bool operator()(X a, X b)
   {
      return c(f(a),g(b));
   }
};
*/

void test05()
{
   point3<double> p1(.2,.5,.7),p2(0,0,0),p3(1,3,5);
   dist02<double,double&, const point3<double>& > f;

   cout << SHOW( f(p1) ) << endl;
   cout << SHOW( f(p2) ) << endl;
   cout << SHOW( f(p3) ) << endl;

   compare<
      less<double>, 
      dist02<double,double&, const point3<double>& >,
      point3<double> > c;

   cout << endl;
   cout << SHOW( c(p1,p2) ) << endl;
   cout << SHOW( c(p1,p3) ) << endl;
   cout << SHOW( c(p2,p1) ) << endl;
   cout << SHOW( c(p2,p3) ) << endl;
   cout << SHOW( c(p3,p1) ) << endl;
   cout << SHOW( c(p3,p2) ) << endl;
}

void test06()
{
   // Same as test03(), but using compare object
   // sorting a vector

   // creating a vector with random points
   const unsigned int n=10;
   vector< point3<double> > v(n);
   for (unsigned int i=0; i<n; ++i)
      random(v[i]);

   compare<
      less<double>,
      dist02<double,double&, const point3<double>& >,
      point3<double> > c;   
   sort(v.begin(),v.end(),c);

   // Displaying the result
   cout << endl;
   cout << printvecwrap<point3<double> >(2,v) << endl;
   dist02<double,double&, const point3<double>& > f;
   vector<double> vd;
   mapping(vd,v,f);
   cout << printvecwrap<double>(5,vd) << endl;
}
   

void main()
{
   test06();

};

