#include "lp.h"

#include <assert.h>
#include <math.h>
#include <string>

#include "timer.h"

static Timer lpTimer("LP",100);
static Timer lpTimer2("LP2",100);
static Timer lpTimer3("LP3 - rank of matrix",100);

LpSolver *LpSolver::list;


LpSolver::LpSolver()
{
   next=list;
   list=this;
}


LpSolver *LpSolver::find(const char *name)
{
   LpSolver *l=list;
   while(l)
      {
         if(std::string(l->name())==std::string(name))break;
         l=l->next;
      }
   return l;
}


void LpSolver::printList(FILE *f)
{
   fprintf(f,"List of linked LP solvers:\n");
   LpSolver *l=list;
   while(l)
      {
         fprintf(f," %s\n",l->name());
         l=l->next;
      }
}


bool LpSolver::interiorPoint(const IntegerVectorList &g, IntegerVector &result, bool strictlyPositive, IntegerVector const *equalitySet)
{
  fprintf(stderr,"interiorPoint method not supported in \"%s\" LP class\n",name());
  assert(0);

  return false;
}


bool LpSolver::hasInteriorPoint(const IntegerVectorList &g, bool strictlyPositive, IntegerVector const *equalitySet)
{
  fprintf(stderr,"hasInteriorPoint method not supported in \"%s\" LP class\n",name());
  assert(0);
}


IntegerVectorList::const_iterator LpSolver::shoot(const IntegerVectorList &g)
{
  fprintf(stderr,"shoot method not supported in \"%s\" LP class\n",name());
  assert(0);
  return g.begin();
}



bool LpSolver::positiveVectorInKernel(const IntegerVectorList &g, IntegerVector *result)
{
  fprintf(stderr,"positiveVectorInKernel method not supported in \"%s\" LP class\n",name());
  assert(0);
  return false;
}


int LpSolver::rankOfMatrix(const IntegerVectorList &g)
{
  fprintf(stderr,"rankOfMatrix method not supported in \"%s\" LP class\n",name());
  assert(0);
  return 0;
}


IntegerVectorList LpSolver::extremeRaysInequalityIndices(const IntegerVectorList &inequalityList)
{
  fprintf(stderr,"extremeRaysInequalityIndices not supported in \"%s\" LP class\n",name());
  assert(0);
  return IntegerVectorList();
}


void LpSolver::removeRedundantRows(IntegerVectorList *inequalities, IntegerVectorList *equalities, bool removeInequalityRedundancies)
{
  fprintf(stderr,"removeRedundantRows method not supported in \"%s\" LP class\n",name());
  assert(0);
}

IntegerVector LpSolver::relativeInteriorPoint(const IntegerVectorList &g, IntegerVector const *equalitySet)
{
  fprintf(stderr,"relativeInteriorPoint method not supported in \"%s\" LP class\n",name());
  assert(0);
  return IntegerVector();
}

void LpSolver::dual(int n, const IntegerVectorList &inequalities, const IntegerVectorList &equations, IntegerVectorList *dualInequalities, IntegerVectorList *dualEquations)
{
  fprintf(stderr,"dual method not supported in \"%s\" LP class\n",name());
  assert(0);
}

static LpSolver *soplex,*huber,*cdd,*cddgmp,*default_;
static bool initialized;


bool lpSetSolver(const char *name)
{
  soplex=LpSolver::find("SoPlex");
  huber=LpSolver::find("Huber's");
  cdd=LpSolver::find("cdd");
  cddgmp=LpSolver::find("cddgmp");
  LpSolver *selected=LpSolver::find(name);
  default_=huber;
  if(soplex)default_=soplex;
  if(cdd)default_=cdd;
  if(selected)default_=selected;
  initialized=true;
  assert(default_);
  fprintf(stderr,"LP algorithm being used: \"%s\".\n",default_->name());

  return selected;
}


static void LpInit()
{
   if(!initialized)
      {
	lpSetSolver("");
      }
}


bool isFacet(const IntegerVectorList &g, IntegerVectorList::const_iterator i)
{
  TimerScope ts(&lpTimer);
  LpInit();

  return default_->isFacet(g,i); 
}


bool interiorPoint(const IntegerVectorList &g, IntegerVector &result, bool strictlyPositive, IntegerVector const *equalitySet)
{
  LpInit();
  return default_->interiorPoint(g,result,strictlyPositive,equalitySet);
}


bool hasInteriorPoint(const IntegerVectorList &g, bool strictlyPositive, IntegerVector const *equalitySet)
{
  LpInit();
  return default_->hasInteriorPoint(g,strictlyPositive, equalitySet);
}


IntegerVectorList::const_iterator shootRay(const IntegerVectorList &g)
{
  TimerScope ts(&lpTimer2);
  LpInit();

  if(g.empty())return g.end();

  return default_->shoot(g); 
}


bool positiveVectorInKernel(const IntegerVectorList &g, IntegerVector *result)
{
  LpInit();

  return default_->positiveVectorInKernel(g,result);
}


int rankOfMatrix(const IntegerVectorList &g)
{
  TimerScope ts(&lpTimer3);
  LpInit();

  return default_->rankOfMatrix(g);
}


IntegerVectorList extremeRaysInequalityIndices(const IntegerVectorList &inequalityList)
{
  LpInit();

  return default_->extremeRaysInequalityIndices(inequalityList);
}


void removeRedundantRows(IntegerVectorList *inequalities, IntegerVectorList *equalities, bool removeInequalityRedundancies)
{
  LpInit();

  return default_->removeRedundantRows(inequalities,equalities,removeInequalityRedundancies);
}


IntegerVector relativeInteriorPoint(const IntegerVectorList &g, IntegerVector const *equalitySet)
{
  LpInit();

  return default_->relativeInteriorPoint(g,equalitySet);
}


void dual(int n, const IntegerVectorList &inequalities, const IntegerVectorList &equations, IntegerVectorList *dualInequalities, IntegerVectorList *dualEquations)
{
  LpInit();

  return default_->dual(n,inequalities,equations,dualInequalities,dualEquations);
}
