#include<lemon/lp_skeleton.h>
#include "test_tools.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef HAVE_GLPK
#include <lemon/lp_glpk.h>
#endif

#ifdef HAVE_CPLEX
#include <lemon/lp_cplex.h>
#endif

using namespace lemon;

void lpTest(LpSolverBase & lp)
{
  typedef LpSolverBase LP;

  std::vector<LP::Col> x(10);
  //  for(int i=0;i<10;i++) x.push_back(lp.addCol());
  lp.addColSet(x);

  std::vector<LP::Col> y(10);
  lp.addColSet(y);

  std::map<int,LP::Col> z;
  
  z.insert(std::make_pair(12,INVALID));
  z.insert(std::make_pair(2,INVALID));
  z.insert(std::make_pair(7,INVALID));
  z.insert(std::make_pair(5,INVALID));
  
  lp.addColSet(z);

  {
    LP::Expr e,f,g;
    LP::Col p1,p2,p3,p4,p5;
    LP::Constr c;
    
    e[p1]=2;
    e.constComp()=12;
    e[p1]+=2;
    e.constComp()+=12;
    e[p1]-=2;
    e.constComp()-=12;
    
    e=2;
    e=2.2;
    e=p1;
    e=f;
    
    e+=2;
    e+=2.2;
    e+=p1;
    e+=f;
    
    e-=2;
    e-=2.2;
    e-=p1;
    e-=f;
    
    e*=2;
    e*=2.2;
    e/=2;
    e/=2.2;
    
    e=((p1+p2)+(p1-p2)+(p1+12)+(12+p1)+(p1-12)+(12-p1)+
       (f+12)+(12+f)+(p1+f)+(f+p1)+(f+g)+
       (f-12)+(12-f)+(p1-f)+(f-p1)+(f-g)+
       2.2*f+f*2.2+f/2.2+
       2*f+f*2+f/2+
       2.2*p1+p1*2.2+p1/2.2+
       2*p1+p1*2+p1/2
       );


    c = (e  <= f  );
    c = (e  <= 2.2);
    c = (e  <= 2  );
    c = (e  <= p1 );
    c = (2.2<= f  );
    c = (2  <= f  );
    c = (p1 <= f  );
    c = (p1 <= p2 );
    c = (p1 <= 2.2);
    c = (p1 <= 2  );
    c = (2.2<= p2 );
    c = (2  <= p2 );
    
    c = (e  >= f  );
    c = (e  >= 2.2);
    c = (e  >= 2  );
    c = (e  >= p1 );
    c = (2.2>= f  );
    c = (2  >= f  );
    c = (p1 >= f  );
    c = (p1 >= p2 );
    c = (p1 >= 2.2);
    c = (p1 >= 2  );
    c = (2.2>= p2 );
    c = (2  >= p2 );
    
    c = (e  == f  );
    c = (e  == 2.2);
    c = (e  == 2  );
    c = (e  == p1 );
    c = (2.2== f  );
    c = (2  == f  );
    c = (p1 == f  );
    //c = (p1 == p2 );
    c = (p1 == 2.2);
    c = (p1 == 2  );
    c = (2.2== p2 );
    c = (2  == p2 );
    
    c = (2 <= e <= 3);
    c = (2 <= p1<= 3);
    
    c = (2 >= e >= 3);
    c = (2 >= p1>= 3);
    
    e[x[3]]=2;
    e[x[3]]=4;
    e[x[3]]=1;
    e.constComp()=12;
    
    lp.addRow(LP::INF,e,23);
    lp.addRow(LP::INF,3.0*(x[1]+x[2]/2)-x[3],23);
    lp.addRow(LP::INF,3.0*(x[1]+x[2]*2-5*x[3]+12-x[4]/3)+2*x[4]-4,23);
    
    lp.addRow(x[1]+x[3]<=x[5]-3);
    lp.addRow(-7<=x[1]+x[3]-12<=3);
    lp.addRow(x[1]<=x[5]);
  }
  
  {
    LP::DualExpr e,f,g;
    LP::Row p1,p2,p3,p4,p5;
    
    e[p1]=2;
    e[p1]+=2;
    e[p1]-=2;
    
    e=p1;
    e=f;
    
    e+=p1;
    e+=f;
    
    e-=p1;
    e-=f;
    
    e*=2;
    e*=2.2;
    e/=2;
    e/=2.2;
    
    e=((p1+p2)+(p1-p2)+(p1+12)+(12+p1)+(p1-12)+(12-p1)+
       (p1+f)+(f+p1)+(f+g)+
       (p1-f)+(f-p1)+(f-g)+
       2.2*f+f*2.2+f/2.2+
       2*f+f*2+f/2+
       2.2*p1+p1*2.2+p1/2.2+
       2*p1+p1*2+p1/2
       );
  }
  

}

void aTest(LpSolverBase & lp)
{
  typedef LpSolverBase LP;

 //The following example is taken from the book by Gáspár and Temesi, page 39.

  typedef LpSolverBase::Row Row;
  typedef LpSolverBase::Col Col;


  Col x1 = lp.addCol();
  Col x2 = lp.addCol();


  //Constraints
  lp.addRow(3*x1+2*x2 >=6);  
  lp.addRow(-1*x1+x2<=4);  
  lp.addRow(5*x1+8*x2<=40);  
  lp.addRow(x1-2*x2<=4);  
  //Nonnegativity of the variables
  lp.colLowerBound(x1, 0);
  lp.colLowerBound(x2, 0);
  //Objective function
  lp.setObj(2*x1+x2);

  lp.max();
  lp.solve();


  if (lp.primalStatus()==LpSolverBase::OPTIMAL){
    printf("Z = %g; x1 = %g; x2 = %g\n", 
	   lp.primalValue(), 
	   lp.primal(x1), lp.primal(x2));
  }
  else{
    std::cout<<"Optimal solution not found!"<<std::endl;
  }

  check(lp.primalStatus()==LpSolverBase::OPTIMAL,"Primalstatus should be OPTIMAL");

  double opt=123/9;
  check(lp.primalValue()==opt,"The optimum value is 122/9");


}


int main() 
{
  LpSkeleton lp_skel;
  lpTest(lp_skel);

#ifdef HAVE_GLPK
  LpGlpk lp_glpk;
  lpTest(lp_glpk);
  aTest(lp_glpk);
#endif

#ifdef HAVE_CPLEX
//  LpCplex lp_cplex;
//  lpTest(lp_cplex);
#endif

  return 0;
}
