/**
 * \brief Solve an instance of the "Variable Placement with Separation
 * Constraints" problem.
 *
 * Authors:
 *   Tim Dwyer <tgdwyer@gmail.com>
 *
 * Copyright (C) 2005 Authors
 * 
 * This version is released under the CPL (Common Public License) with
 * the Graphviz distribution.
 * A version is also available under the LGPL as part of the Adaptagrams
 * project: http://sourceforge.net/projects/adaptagrams.  
 * If you make improvements or bug fixes to this code it would be much
 * appreciated if you could also contribute those changes back to the
 * Adaptagrams repository.
 */

#include <cassert>
#include "constraint.h"
#include "block.h"
#include "blocks.h"
#include "solve_VPSC.h"
#include <math.h>
#include <sstream>
#ifdef RECTANGLE_OVERLAP_LOGGING
#include <fstream>
using std::ios;
using std::ofstream;
using std::endl;
#endif

using std::ostringstream;
using std::list;
using std::set;

IncVPSC::IncVPSC(const unsigned n, Variable *vs[], const unsigned m, Constraint *cs[]) 
	: VPSC(n,vs,m,cs) {
	inactive.assign(cs,cs+m);
	for(ConstraintList::iterator i=inactive.begin();i!=inactive.end();i++) {
		(*i)->active=false;
	}
}
VPSC::VPSC(const unsigned n, Variable *vs[], const unsigned m, Constraint *cs[]) : cs(cs), m(m) {
	bs=new Blocks(n, vs);
#ifdef RECTANGLE_OVERLAP_LOGGING
	printBlocks();
	assert(!constraintGraphIsCyclic(n,vs));
#endif
}
VPSC::~VPSC() {
	delete bs;
}

// useful in debugging
void VPSC::printBlocks() {
#ifdef RECTANGLE_OVERLAP_LOGGING
	ofstream f(LOGFILE,ios::app);
	for(set<Block*>::iterator i=bs->begin();i!=bs->end();i++) {
		Block *b=*i;
		f<<"  "<<*b<<endl;
	}
	for(unsigned i=0;i<m;i++) {
		f<<"  "<<*cs[i]<<endl;
	}
#endif
}
/**
* Produces a feasible - though not necessarily optimal - solution by
* examining blocks in the partial order defined by the directed acyclic
* graph of constraints. For each block (when processing left to right) we
* maintain the invariant that all constraints to the left of the block
* (incoming constraints) are satisfied. This is done by repeatedly merging
* blocks into bigger blocks across violated constraints (most violated
* first) fixing the position of variables inside blocks relative to one
* another so that constraints internal to the block are satisfied.
*/
void VPSC::satisfy() {
	list<Variable*> *vs=bs->totalOrder();
	for(list<Variable*>::iterator i=vs->begin();i!=vs->end();i++) {
		Variable *v=*i;
		if(!v->block->deleted) {
			bs->mergeLeft(v->block);
		}
	}
	bs->cleanup();
	for(unsigned i=0;i<m;i++) {
		if(cs[i]->slack()<-0.0000001) {
#ifdef RECTANGLE_OVERLAP_LOGGING
			ofstream f(LOGFILE,ios::app);
			f<<"Error: Unsatisfied constraint: "<<*cs[i]<<endl;
#endif
			//assert(cs[i]->slack()>-0.0000001);
			throw "Unsatisfied constraint";
		}
	}
	delete vs;
}

void VPSC::refine() {
	bool solved=false;
	// Solve shouldn't loop indefinitely
	// ... but just to make sure we limit the number of iterations
	unsigned maxtries=100;
	while(!solved&&maxtries>=0) {
		solved=true;
		maxtries--;
		for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
			Block *b=*i;
			b->setUpInConstraints();
			b->setUpOutConstraints();
		}
		for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
			Block *b=*i;
			Constraint *c=b->findMinLM();
			if(c!=NULL && c->lm<0) {
#ifdef RECTANGLE_OVERLAP_LOGGING
				ofstream f(LOGFILE,ios::app);
				f<<"Split on constraint: "<<*c<<endl;
#endif
				// Split on c
				Block *l=NULL, *r=NULL;
				bs->split(b,l,r,c);
				bs->cleanup();
				// split alters the block set so we have to restart
				solved=false;
				break;
			}
		}
	}
	for(unsigned i=0;i<m;i++) {
		if(cs[i]->slack()<-0.0000001) {
			assert(cs[i]->slack()>-0.0000001);
			throw "Unsatisfied constraint";
		}
	}
}
/**
 * Calculate the optimal solution. After using satisfy() to produce a
 * feasible solution, refine() examines each block to see if further
 * refinement is possible by splitting the block. This is done repeatedly
 * until no further improvement is possible.
 */
void VPSC::solve() {
	satisfy();
	refine();
}

void IncVPSC::solve() {
#ifdef RECTANGLE_OVERLAP_LOGGING
	ofstream f(LOGFILE,ios::app);
	f<<"solve_inc()..."<<endl;
#endif
	double lastcost,cost = bs->cost();
	do {
		lastcost=cost;
		satisfy();
		splitBlocks();
		cost = bs->cost();
#ifdef RECTANGLE_OVERLAP_LOGGING
	f<<"  cost="<<cost<<endl;
#endif
	} while(fabs(lastcost-cost)>0.0001);
}
/**
 * incremental version of satisfy that allows refinement after blocks are
 * moved.
 *
 *  - move blocks to new positions
 *  - repeatedly merge across most violated constraint until no more
 *    violated constraints exist
 *
 * Note: there is a special case to handle when the most violated constraint
 * is between two variables in the same block.  Then, we must split the block
 * over an active constraint between the two variables.  We choose the 
 * constraint with the most negative lagrangian multiplier. 
 */
void IncVPSC::satisfy() {
#ifdef RECTANGLE_OVERLAP_LOGGING
	ofstream f(LOGFILE,ios::app);
	f<<"satisfy_inc()..."<<endl;
#endif
	splitBlocks();
	long splitCtr = 0;
	Constraint* v = NULL;
	while(mostViolated(inactive,v)<-0.0000001) {
		assert(!v->active);
		Block *lb = v->left->block, *rb = v->right->block;
		if(lb != rb) {
			lb->merge(rb,v);
		} else {
			if(splitCtr++>10000) {
				throw "Cycle Error!";
			}
			// constraint is within block, need to split first
			inactive.push_back(lb->splitBetween(v->left,v->right,lb,rb));
			lb->merge(rb,v);
			bs->insert(lb);
		}
	}
#ifdef RECTANGLE_OVERLAP_LOGGING
	f<<"  finished merges."<<endl;
#endif
	bs->cleanup();
	for(unsigned i=0;i<m;i++) {
		v=cs[i];
		if(v->slack()<-0.0000001) {
			//assert(cs[i]->slack()>-0.0000001);
			ostringstream s;
			s<<"Unsatisfied constraint: "<<*v;
			throw s.str().c_str();
		}
	}
#ifdef RECTANGLE_OVERLAP_LOGGING
	f<<"  finished cleanup."<<endl;
	printBlocks();
#endif
}
void IncVPSC::moveBlocks() {
#ifdef RECTANGLE_OVERLAP_LOGGING
	ofstream f(LOGFILE,ios::app);
	f<<"moveBlocks()..."<<endl;
#endif
	for(set<Block*>::const_iterator i(bs->begin());i!=bs->end();i++) {
		Block *b = *i;
		b->wposn = b->desiredWeightedPosition();
		b->posn = b->wposn / b->weight;
	}
#ifdef RECTANGLE_OVERLAP_LOGGING
	f<<"  moved blocks."<<endl;
#endif
}
void IncVPSC::splitBlocks() {
#ifdef RECTANGLE_OVERLAP_LOGGING
	ofstream f(LOGFILE,ios::app);
#endif
	moveBlocks();
	splitCnt=0;
	// Split each block if necessary on min LM
	for(set<Block*>::const_iterator i(bs->begin());i!=bs->end();i++) {
		Block* b = *i;
		Constraint* v=b->findMinLM();
		if(v!=NULL && v->lm < -0.0000001) {
#ifdef RECTANGLE_OVERLAP_LOGGING
			f<<"    found split point: "<<*v<<" lm="<<v->lm<<endl;
#endif
			splitCnt++;
			Block *b = v->left->block, *l=NULL, *r=NULL;
			assert(v->left->block == v->right->block);
			double pos = b->posn;
			b->split(l,r,v);
			l->posn=r->posn=pos;
			l->wposn = l->posn * l->weight;
			r->wposn = r->posn * r->weight;
			bs->insert(l);
			bs->insert(r);
			b->deleted=true;
			inactive.push_back(v);
#ifdef RECTANGLE_OVERLAP_LOGGING
			f<<"  new blocks: "<<*l<<" and "<<*r<<endl;
#endif
		}
	}
#ifdef RECTANGLE_OVERLAP_LOGGING
	f<<"  finished splits."<<endl;
#endif
	bs->cleanup();
}

/**
 * Scan constraint list for the most violated constraint, or the first equality
 * constraint
 */
double IncVPSC::mostViolated(ConstraintList &l, Constraint* &v) {
	double minSlack = DBL_MAX;
#ifdef RECTANGLE_OVERLAP_LOGGING
	ofstream f(LOGFILE,ios::app);
	f<<"Looking for most violated..."<<endl;
#endif
	ConstraintList::iterator end = l.end();
	ConstraintList::iterator deletePoint = end;
	for(ConstraintList::iterator i=l.begin();i!=end;i++) {
		Constraint *c=*i;
		double slack = c->slack();
		if(c->equality || slack < minSlack) {
			minSlack=slack;	
			v=c;
			deletePoint=i;
			if(c->equality) break;
		}
	}
	// Because the constraint list is not order dependent we just
	// move the last element over the deletePoint and resize
	// downwards.  There is always at least 1 element in the
	// vector because of search.
	if(deletePoint != end && minSlack<-0.0000001) {
		*deletePoint = l[l.size()-1];
		l.resize(l.size()-1);
	}
#ifdef RECTANGLE_OVERLAP_LOGGING
	f<<"  most violated is: "<<*v<<endl;
#endif
	return minSlack;
}

#include <map>
using std::map;
using std::vector;
struct node {
	set<node*> in;
	set<node*> out;
};
// useful in debugging - cycles would be BAD
bool VPSC::constraintGraphIsCyclic(const unsigned n, Variable *vs[]) {
	map<Variable*, node*> varmap;
	vector<node*> graph;
	for(unsigned i=0;i<n;i++) {
		node *u=new node;
		graph.push_back(u);
		varmap[vs[i]]=u;
	}
	for(unsigned i=0;i<n;i++) {
		for(vector<Constraint*>::iterator c=vs[i]->in.begin();c!=vs[i]->in.end();c++) {
			Variable *l=(*c)->left;
			varmap[vs[i]]->in.insert(varmap[l]);
		}

		for(vector<Constraint*>::iterator c=vs[i]->out.begin();c!=vs[i]->out.end();c++) {
			Variable *r=(*c)->right;
			varmap[vs[i]]->out.insert(varmap[r]);
		}
	}
	while(graph.size()>0) {
		node *u=NULL;
		vector<node*>::iterator i=graph.begin();
		for(;i!=graph.end();i++) {
			u=*i;
			if(u->in.size()==0) {
				break;
			}
		}
		if(i==graph.end() && graph.size()>0) {
			//cycle found!
			return true;
		} else {
			graph.erase(i);
			for(set<node*>::iterator j=u->out.begin();j!=u->out.end();j++) {
				node *v=*j;
				v->in.erase(u);
			}
			delete u;
		}
	}
	for(unsigned i=0; i<graph.size(); i++) {
		delete graph[i];
	}
	return false;
}

// useful in debugging - cycles would be BAD
bool VPSC::blockGraphIsCyclic() {
	map<Block*, node*> bmap;
	vector<node*> graph;
	for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
		Block *b=*i;
		node *u=new node;
		graph.push_back(u);
		bmap[b]=u;
	}
	for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
		Block *b=*i;
		b->setUpInConstraints();
		Constraint *c=b->findMinInConstraint();
		while(c!=NULL) {
			Block *l=c->left->block;
			bmap[b]->in.insert(bmap[l]);
			b->deleteMinInConstraint();
			c=b->findMinInConstraint();
		}

		b->setUpOutConstraints();
		c=b->findMinOutConstraint();
		while(c!=NULL) {
			Block *r=c->right->block;
			bmap[b]->out.insert(bmap[r]);
			b->deleteMinOutConstraint();
			c=b->findMinOutConstraint();
		}
	}
	while(graph.size()>0) {
		node *u=NULL;
		vector<node*>::iterator i=graph.begin();
		for(;i!=graph.end();i++) {
			u=*i;
			if(u->in.size()==0) {
				break;
			}
		}
		if(i==graph.end() && graph.size()>0) {
			//cycle found!
			return true;
		} else {
			graph.erase(i);
			for(set<node*>::iterator j=u->out.begin();j!=u->out.end();j++) {
				node *v=*j;
				v->in.erase(u);
			}
			delete u;
		}
	}
	for(unsigned i=0; i<graph.size(); i++) {
		delete graph[i];
	}
	return false;
}

