#include  <iostream>
#include  <cstdlib>
#include  <vector>
#include  <ctime>
#include  <random>
#define TRUE 1
#define FALSE 0
using namespace std;


unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine e(seed);

class Coordinate
{
public:
  Coordinate( Coordinate *);
  Coordinate( double, double );
  Coordinate(){x=0;y=0;};
  void set(double, double);
  void set(Coordinate *);
  double getx(){return x;};
  double gety(){return y;};
  double distancefrom(Coordinate*);
  double distancefrom(double,double);
  friend ostream &operator << (ostream &out, const Coordinate &c); 
  //friend istream &operator >> (istream &input, Coordinate &c );

  bool operator==(const Coordinate& c) const{return ( (c.x == x) && (c.y == y)) ;}
  ~Coordinate();
private:
  double x;
  double y;
};
ostream & operator << (ostream &out, const Coordinate &c) 
{
  out << "(" << c.x << "," << c.y << ")"; 
  return out; 
}

//istream & operator >> (istream &input, Coordinate &c)
//{ 
//  input >> c.x >> c.y;
//  return input;            
//}

Coordinate::~Coordinate()
{
#ifdef DEBUG
  cerr << "Deleteing coordinate " << *this << endl;
#endif
}
void Coordinate::set( Coordinate * c )
{
  x=c->getx();
  y=c->gety();
  return;
}

Coordinate::Coordinate( double x, double y )
{
#ifdef DEBUG
  cerr << "[ Coordinate Constructor(" << x << "," << y << ") ]" << endl;
#endif
  this->x = x;
  this->y = y;

  return;
}
Coordinate::Coordinate( Coordinate * c )
{
#ifdef DEBUG
  cerr << "[ Coordinate Constructor(Coordinate * c) ]" << endl;
#endif
  
  this->x = c->getx();
  this->y = c->gety();

  return;
}

  
void Coordinate::set(double x, double y)
{
#ifdef DEBUG
  cerr << "[ Coordinate::set(double,double) ]" << endl;
#endif
  this->x = x;
  this->y = y;
  return;
}

double Coordinate::distancefrom(Coordinate* c)
{
#ifdef DEBUG
  cerr << "[calculate distance from " << *c << " to " << *this << "]" << endl;
#endif
  double x1 = c->getx();
  double y1 = c->gety();
  return sqrt( ( x1 - this->x )*( x1 - this->x ) + ( y1 - this->y)*( y1 - this->y) );
}

double Coordinate::distancefrom(double x1, double y1)
{
#ifdef DEBUG
  cerr << "[calculate distance from (" << x1 << "," << y1 << ") to " << *this << "]" << endl;
#endif
  return sqrt( ( x1 - this->x )*( x1 - this->x ) + ( y1 - this->y)*( y1 - this->y) );
}


class NeuralNode
{
public:
  NeuralNode(NeuralNode*);
  NeuralNode(double,double);
  NeuralNode(Coordinate*);
  ~NeuralNode();

  void addLink(NeuralNode*);
  void addOneWayLink(NeuralNode*);
  vector<NeuralNode*> getLinks(){ return pLinks; };
  void removeLink(NeuralNode*);
  
  int howManyLinks(){ return pLinks.size(); };

  string getName(){ return name; };
  void setName( string s ){ name = s;};

  Coordinate * getCoordinate(){ return pCoordinate; };
  void setCoordinate(double x, double y);
  void setCoordinate( Coordinate * c );
  friend ostream &operator << (ostream &out, const NeuralNode &n); 

  NeuralNode * getParent(){ return pParent; };
  void setParent( NeuralNode * N ){ pParent = N; };

  void shuffle(){ std::shuffle( pLinks.begin(), pLinks.end(), e ); };
private:
  NeuralNode(void);
  vector<NeuralNode*> pLinks;
  string name;
  Coordinate * pCoordinate;
  NeuralNode * pParent;
};
NeuralNode::~NeuralNode()
{
#ifdef DEBUG
  cerr << "Deleting Node: " << *pCoordinate << endl;
#endif
  delete pCoordinate;
}
void NeuralNode::removeLink( NeuralNode* N )
{
  
  Coordinate * tmpCoord = N->getCoordinate();
  for( int i=0; i<pLinks.size(); i++ )
    {
      if( *tmpCoord == *pLinks[i]->getCoordinate() )
	{
#ifdef DEBUG
	  cerr << "removing " << *pLinks[i] << " from " << name << endl;
#endif
	  pLinks.erase(pLinks.begin()+i);
	}
    }
  

}
void NeuralNode::addLink( NeuralNode* N )
{
  bool okaytoadd = TRUE;
  // if link already exists then don't add it
  for( int i=0; i<pLinks.size(); i++ )
    {
      if( pLinks[i]->getCoordinate() == N->getCoordinate() )
	{
	  cerr << "not okay to add link from " << *N << " to " << *pLinks[i] << " because one already exists" << endl;
	  okaytoadd = FALSE;
	}
    }
  if( *N->getCoordinate() == *pCoordinate )
    {
      cerr << "cannot link to one's self!" << endl;
    }
  else
    {
      if( okaytoadd )
	{
	  pLinks.push_back(N);
	  N->addOneWayLink(this);
	}
    }
  return;
}

void NeuralNode::addOneWayLink( NeuralNode* N )
{
  bool okaytoadd = TRUE;
  // if link already exists then don't add it
  for( int i=0; i<pLinks.size(); i++ )
    {
      if( pLinks[i]->getCoordinate() == N->getCoordinate() )
	{
	  cerr << "not okay to add this link because one already exists" << endl;
	  okaytoadd = FALSE;
	}
    }
    
  if( *N->getCoordinate() == *pCoordinate )
    {
      cerr << "cannot link to one's self!" << endl;
    }
  else
    {
      if( okaytoadd ) pLinks.push_back(N);
    }
  return;
}

NeuralNode::NeuralNode(double x, double y)
{
  pCoordinate=new Coordinate(x,y);
  pParent=NULL;
  return;
}
NeuralNode::NeuralNode(Coordinate * c)
{
  // make a copy of c -- this allows c to be deleted w/o modifying Neural Node
  if(pCoordinate != NULL) delete pCoordinate;
  pCoordinate=new Coordinate( c->getx(), c->gety() );
  pParent=NULL;
  return;
}
NeuralNode::NeuralNode()
{
  pCoordinate=new Coordinate( 0, 0 );
  pParent=NULL;
  return;
}
NeuralNode::NeuralNode(NeuralNode* N)
{
  if( pCoordinate == NULL )
    {
      pCoordinate = new Coordinate( N->getCoordinate() );
    }
  else
    {
      pCoordinate->set( N->getCoordinate() );
    }
  name = "";
  pParent = N->getParent();
  return;
}

void NeuralNode::setCoordinate( Coordinate * c )
{
  if( pCoordinate != NULL ) delete pCoordinate;
  pCoordinate = new Coordinate( c->getx(), c->gety() );
  // pCoordinate->setname( c->getName() );      
  return;
}

ostream & operator << (ostream &out, const NeuralNode &c) 
{
  out << *c.pCoordinate << " [ ";
  for( int i=0; i<c.pLinks.size(); i++ )
    {
      out << *c.pLinks[i]->getCoordinate();
    }
  out << " ] (" << c.name << ")"; 
  return out; 
}


class Path
{
public:
  Path(){};
  ~Path(){};
  double getLength(){ return length; };
  void setLength( double l ){ length=l; };
  vector<NeuralNode*> getPath(){ return pPath; };
  void addNode( NeuralNode* N){pPath.push_back(N);};
  void clear(){ pPath.clear();};
  int size(){ return pPath.size(); };
  void reverse(){ std::reverse( pPath.begin(), pPath.end());};
private:
  vector<NeuralNode*> pPath;
  double length;
};

  
  
  
  
vector<NeuralNode*> breadcrumbs;
vector<NeuralNode*> viewableworld;
vector<NeuralNode*> path;
Path * mypath;
double path_length=0;
int MODE=0;

void explore( NeuralNode * N)
{
#ifdef DEBUG
  cerr << "exploring: " << *N << " which has " << N->howManyLinks() << " links" << endl;
#endif
  breadcrumbs.push_back( N );
  
  if( viewableworld.size() == 0 ) viewableworld.push_back( N );
  
  // Explore Links from 0 to how Many There Are
  // *********************

  if( MODE == 0 )
    {
      for( int i=0; i< N->howManyLinks(); i++ )
	{
	  bool alreadyexplored=false;
	  
	  // check to make sure that N->getLinks[i] is not in breadcrumbs yet.
	  for( int j=0; j<breadcrumbs.size(); j++ )
	    {
	      if( *breadcrumbs[j]->getCoordinate() == *N->getLinks()[i]->getCoordinate() )
		{
#ifdef DEBUG
		  cerr << "found " << *N->getLinks()[i]->getCoordinate() << " in breadcrumbs - don't re-explore" << endl;
#endif
		  alreadyexplored=true;
		}
	    }
	  if( !alreadyexplored )
	    {
	      // add the new one to the viewable world list
	      N->getLinks()[i]->setParent(N);
	      //cerr << endl << *N << endl << endl;
	      viewableworld.push_back( N->getLinks()[i] );
	      explore( N->getLinks()[i] );
	    }
	}
    }
  else if (MODE==1 )
    //************************** EXPLORE MODE = 1;
    {
      for( int i=N->howManyLinks()-1; i>=0; i-- )
	{
	  bool alreadyexplored=false;
	  
	  // check to make sure that N->getLinks[i] is not in breadcrumbs yet.
	  for( int j=0; j<breadcrumbs.size(); j++ )
	    {
	      if( *breadcrumbs[j]->getCoordinate() == *N->getLinks()[i]->getCoordinate() )
		{
#ifdef DEBUG
		  cerr << "found " << *N->getLinks()[i]->getCoordinate() << " in breadcrumbs - don't re-explore" << endl;
#endif
		  alreadyexplored=true;
		}
	    }
	  if( !alreadyexplored )
	    {
	      // add the new one to the viewable world list
	      N->getLinks()[i]->setParent(N);
	      //cerr << endl << *N << endl << endl;
	      viewableworld.push_back( N->getLinks()[i] );
	      explore( N->getLinks()[i]);
	    }
	}
    }
  // ************************************
  else if ( MODE==2 )
    //************************** EXPLORE MODE = 2;
    {
            N->shuffle();

      for( int i=0; i< N->howManyLinks(); i++ )
	{
	  bool alreadyexplored=false;
	  
	  // check to make sure that N->getLinks[i] is not in breadcrumbs yet.
	  for( int j=0; j<breadcrumbs.size(); j++ )
	    {
	      if( *breadcrumbs[j]->getCoordinate() == *N->getLinks()[i]->getCoordinate() )
		{
#ifdef DEBUG
		  cerr << "found " << *N->getLinks()[i]->getCoordinate() << " in breadcrumbs - don't re-explore" << endl;
#endif
		  alreadyexplored=true;
		}
	    }
	  if( !alreadyexplored )
	    {
	      // add the new one to the viewable world list
	      N->getLinks()[i]->setParent(N);
	      viewableworld.push_back( N->getLinks()[i] );

	      explore( N->getLinks()[i] );
	    }
	}
    }
  // ************************************


  return;
}

bool reachable( NeuralNode * StartNode, NeuralNode * GoalNode )
{
#ifdef DEBUG
  cerr << "looking for: " << *GoalNode << " from " << *StartNode  <<  endl;
#endif
  bool retVal = false;
  explore( StartNode);
  for( int i=0; i<viewableworld.size(); i++ )
    {
      if(*viewableworld[i]->getCoordinate() == *GoalNode->getCoordinate())
	{
	  retVal = TRUE;
	}
    }
  return retVal;
}

Path *  getPath( NeuralNode * start, NeuralNode * goal)
{
  Path * retVal = new Path();
  retVal->clear();
  
  path_length=0;
  breadcrumbs.clear();
  path.clear();
  viewableworld.clear();
  if( reachable( start, goal ) )
    {
      NeuralNode * n = goal;
      Coordinate * a = n->getCoordinate();
      while( n != start )
	{
	  path.push_back( n );
	  n = n->getParent();
	  Coordinate * b = n->getCoordinate();
	  path_length+=a->distancefrom(b);
	  a=b;
	}
      path.push_back( n );
    }
  reverse( path.begin(), path.end() );
  for( int i=0; i<path.size(); i++ )
    {
      retVal->addNode( path[i] );

    }
  retVal->setLength( path_length);
  return retVal;
}

void dumpPath( Path p )
{
  vector<NeuralNode *> myPath = p.getPath();
  
  if( p.size() == 0 ) cerr << "NO ROUTE" << endl << endl;
  for( int i=0; i< p.size()-1; i++ )
    {
      cerr << /* *myPath[i]->getCoordinate()  << */ "(" << myPath[i]->getName() << ")->";
    }
  cerr << /* *myPath[p.size()-1]->getCoordinate() << */ "(" << myPath[p.size()-1]->getName() << ")" << endl;
}

// -------------------------------------------------------


int main()
{

  mypath=new Path();
  vector<NeuralNode*> nodes;

  // Map of Prospect Hill Street Intersections and bends
  // Beverly, Mass. 
  nodes.push_back( new NeuralNode(0,0));
  nodes.push_back( new NeuralNode(58,19));
  nodes.push_back( new NeuralNode(95,48));
  nodes.push_back( new NeuralNode(126,76));
  nodes.push_back( new NeuralNode(159,106));
  nodes.push_back( new NeuralNode(166,3));
  nodes.push_back( new NeuralNode(195,34));
  nodes.push_back( new NeuralNode(213,69));
  nodes.push_back( new NeuralNode(228,101));
  nodes.push_back( new NeuralNode(185,124));
  nodes.push_back( new NeuralNode(214,124));
  nodes.push_back( new NeuralNode(60,105));
  nodes.push_back( new NeuralNode(175,154));
  nodes.push_back( new NeuralNode(59,145));
  nodes.push_back( new NeuralNode(22,163));
  nodes.push_back( new NeuralNode(31,202));
  nodes.push_back( new NeuralNode(25,254));
  nodes.push_back( new NeuralNode(75,269));
  nodes.push_back( new NeuralNode(115,288));
  nodes.push_back( new NeuralNode(162,182));
  nodes.push_back( new NeuralNode(141,220));
  nodes.push_back( new NeuralNode(164,226));
  nodes.push_back( new NeuralNode(181,281));
  nodes.push_back( new NeuralNode(152,310));
  nodes.push_back( new NeuralNode(189,216));
  nodes.push_back( new NeuralNode(236,281));
  nodes.push_back( new NeuralNode(210,195));
  nodes.push_back( new NeuralNode(247,261));
  nodes.push_back( new NeuralNode(215,167));
  nodes.push_back( new NeuralNode(286,173));
  nodes.push_back( new NeuralNode(297,214));
  nodes.push_back( new NeuralNode(280,236));
  nodes.push_back( new NeuralNode(268,303));
  nodes.push_back( new NeuralNode(194,311));
  nodes.push_back( new NeuralNode(176,350));
  nodes.push_back( new NeuralNode(105,368));
  nodes.push_back( new NeuralNode(5,410));
  nodes.push_back( new NeuralNode(41,428));
  nodes.push_back( new NeuralNode(76,448));
  nodes.push_back( new NeuralNode(153,484));
  nodes.push_back( new NeuralNode(100,411));
  nodes.push_back( new NeuralNode(182,455));
  nodes.push_back( new NeuralNode(142,381));
  nodes.push_back( new NeuralNode(190,392));
  nodes.push_back( new NeuralNode(203,432));
  nodes.push_back( new NeuralNode(218,409));
  nodes.push_back( new NeuralNode(187,140));



  
  // *******
  nodes[1]->addLink( nodes[2]);
  nodes[1]->addLink( nodes[11]);
  nodes[2]->addLink( nodes[3]);
  nodes[3]->addLink( nodes[4]);
  nodes[4]->addLink( nodes[7]);
  nodes[5]->addLink( nodes[6]);
  nodes[8]->addLink( nodes[10]);
  nodes[9]->addLink( nodes[10]);
  nodes[10]->addLink( nodes[28]);
  nodes[11]->addLink( nodes[12]);
  nodes[12]->addLink( nodes[19]);
  nodes[13]->addLink( nodes[14]);
  nodes[14]->addLink( nodes[15]);
  nodes[15]->addLink( nodes[16]);
  nodes[16]->addLink( nodes[17]);
  nodes[17]->addLink( nodes[18]);
  nodes[18]->addLink( nodes[20]);
  nodes[21]->addLink( nodes[22]);
  nodes[22]->addLink( nodes[23]);
  nodes[25]->addLink( nodes[27]);
  nodes[26]->addLink( nodes[28]);
  nodes[28]->addLink( nodes[31]);
  nodes[30]->addLink( nodes[31]);
  nodes[32]->addLink( nodes[45]);
  nodes[33]->addLink( nodes[34]);
  nodes[35]->addLink( nodes[42]);
  nodes[36]->addLink( nodes[37]);
  nodes[38]->addLink( nodes[39]);
  nodes[39]->addLink( nodes[41]);
  nodes[43]->addLink( nodes[45]);
  
  nodes[2]->addLink( nodes[5]);
  nodes[3]->addLink( nodes[6]);
  nodes[4]->addLink( nodes[9]);
  nodes[6]->addLink( nodes[7]);
  nodes[7]->addLink( nodes[8]);
  nodes[11]->addLink( nodes[13]);
  nodes[12]->addLink( nodes[46]);
  nodes[13]->addLink( nodes[19]);
  nodes[15]->addLink( nodes[20]);
  nodes[16]->addLink( nodes[36]);
  nodes[17]->addLink( nodes[37]);
  nodes[18]->addLink( nodes[23]);
  nodes[19]->addLink( nodes[24]);
  nodes[20]->addLink( nodes[21]);
  nodes[21]->addLink( nodes[24]);
  nodes[22]->addLink( nodes[25]);
  nodes[23]->addLink( nodes[34]);
  nodes[24]->addLink( nodes[26]);
  nodes[25]->addLink( nodes[32]);
  nodes[27]->addLink( nodes[31]);
  nodes[29]->addLink( nodes[30]);
  nodes[33]->addLink( nodes[43]);
  nodes[34]->addLink( nodes[42]);
  nodes[35]->addLink( nodes[40]);
  nodes[37]->addLink( nodes[38]);
  nodes[38]->addLink( nodes[40]);
  nodes[40]->addLink( nodes[41]);
  nodes[41]->addLink( nodes[44]);
  nodes[42]->addLink( nodes[44]);
  nodes[44]->addLink( nodes[45]);
  
  nodes[18]->addLink( nodes[35]);
  nodes[22]->addLink( nodes[33]);
  nodes[24]->addLink( nodes[27]);
 
  // *******

  nodes[1]->setName("1");
  nodes[2]->setName("2");
  nodes[3]->setName("3");
  nodes[4]->setName("4");
  nodes[5]->setName("5");
  nodes[6]->setName("6");
  nodes[7]->setName("7");
  nodes[8]->setName("8");
  nodes[9]->setName("9");
  nodes[10]->setName("10");
  nodes[11]->setName("11");
  nodes[12]->setName("12");
  nodes[13]->setName("13");
  nodes[14]->setName("14");
  nodes[15]->setName("15");
  nodes[16]->setName("16");
  nodes[17]->setName("17");
  nodes[18]->setName("18");
  nodes[19]->setName("19");
  nodes[20]->setName("20");
  nodes[21]->setName("21");
  nodes[22]->setName("22");
  nodes[23]->setName("23");
  nodes[24]->setName("24");
  nodes[25]->setName("25");
  nodes[26]->setName("26");
  nodes[27]->setName("27");
  nodes[28]->setName("28");
  nodes[29]->setName("29");
  nodes[30]->setName("30");
  nodes[31]->setName("31");
  nodes[32]->setName("32");
  nodes[33]->setName("33");
  nodes[34]->setName("34");
  nodes[35]->setName("35");
  nodes[36]->setName("36");
  nodes[37]->setName("37");
  nodes[38]->setName("38");
  nodes[39]->setName("39");
  nodes[40]->setName("40");
  nodes[41]->setName("41");
  nodes[42]->setName("42");
  nodes[43]->setName("43");
  nodes[44]->setName("44");
  nodes[45]->setName("45");
  nodes[46]->setName("46");
  

  
  //nodes[]->addLink( nodes[] );

  int start_node = 1;
  int goal_node = 39;
    cerr << "Mode: First First" << endl;

  MODE=0;
 Path p = *getPath( nodes[start_node], nodes[goal_node] );   
 dumpPath(p);
 cerr << "Path 1 distance travelled: " << p.getLength() << endl << endl;

 MODE=1;

  cerr << "Mode: First Last" << endl;

 Path q =  *getPath( nodes[start_node], nodes[goal_node] );   
 dumpPath(q);
 cerr << "Path 2 distance travelled: " << q.getLength() << endl << endl;
 
 double minimum_length = 0;
 int minimum_attempt = 0;
 


 MODE=2;
 cerr << "Mode: Brute Force" << endl;
 vector<Path> paths;
 for( int i=0; i<500; i++ )
   {
     paths.push_back( *getPath( nodes[start_node], nodes[goal_node] ) );
     if( minimum_length==0 ) minimum_length = paths[i].getLength();
     if( paths[i].getLength() < minimum_length )
       {
	 minimum_length = paths[i].getLength();
	 minimum_attempt = i;
       };
     
   }

 dumpPath( paths[minimum_attempt] );
 cerr << "Path 3 distance travelled: " << minimum_length << endl << "Attempt #: " << minimum_attempt << endl;

 cerr << endl << endl << endl;
  
  return( 0 );
}