/************************************************************************
* retroLP solves linear programs using the simplex method.             *
* Copyright (C) 2001, 2003, 2005 Gavriel Yarmish & Richard Van Slyke.  *
* For further information, and gpl licensing, see the end of this file.*
************************************************************************/


#include "parameter.hpp"
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <cmath>
#include <string>
#include "long_clock.h"

void ShortHelp();
void LongHelp();
int KeyWordHelp(int, int);
int WhatIs_ptype(std::string pname);
void p_setup();
int UpDate_p(std::string pname, std::string vstring);

// Set up

// integer parameters
const int ni = 8;
ip_attributes ips[ni+1];
long & ccr = ips[1].cvalue;
long & iter_max = ips[2].cvalue;
long & reset_freq = ips[3].cvalue;
long & k_iter = ips[4].cvalue;
long & l_start = ips[5].cvalue;
long & l_ph1 = ips[6].cvalue;
long & l_end = ips[7].cvalue;
long & l_iter = ips[8].cvalue;
// double parameters
const int nd = 9;
dp_attributes dps[nd+1];
double & refresh_factor = dps[1].cvalue;
double & epsc  = dps[2].cvalue;
double & epsp  = dps[3].cvalue;
double & epsi = dps[4].cvalue;
double & epsb = dps[5].cvalue;
double & eps = dps[6].cvalue;
double & del0 = dps[7].cvalue;
double & tau = dps[8].cvalue;
double & delf = dps[9].cvalue;
// boolean parameters
const int nb = 3;
bp_attributes bps[nb+1];
bool & refresh_off = bps[1].cvalue;
bool & rflag = bps[2].cvalue;
bool & max = bps[3].cvalue;
double maxfactor;
// action parameter
const int na = 3;
ap_attributes aps[na+1];

//Global variable for statistics
     long iter = 0,        // iterations in simplex (excluding refresh, et al.)
     fiter = 1,          // count for next reinversion
     miter = 0,       // major iterations: iterations which result in an
                  // actual pivot
     riter = 0,   // Cumulative pivots in refresh, et al.
     cntnfx = 0,      // number of times CCR2 or 3 doesn't encounter fixed, non-basic variable
     cntcc = 0 ;       // number of times CCR2 or 3 actually does detailed calculation

// Support for measuring pivot and running times
// Using clock ticks for short runs; "LongClock" for longer ones
   clock_t pclock,     // Total ticks used in pivots.
           pstart,     // Start of pivot interval
           pstop;      // End of pivot interval
   // Construct a "long" clock.
   LongClock tlong;
   // Construct a "long" clock for measuring pivot times.
   LongClock ptlong;

// To improve locality, we scan matrix alternately left to right
// and right to left, controlled by pdir
  short pdir = -1; // Used in pivot, and pivot+

void p_setup()
{

p_index v;
ip_attributes ip;
dp_attributes dp;
bp_attributes bp;
ap_attributes ap;

// Integer parameters

// CCR
// look_up entry
v.type = INTEGER;
v.index = 1;
look_up["CCR"] = v;
ip.name = "CCR";
ip.cvalue = ip.dvalue  = 2;
ip.help = "CCR = 1: Dantzig column choice, 2: Steepest Edge";
ip.lb = 1;
ip.ub = 2;
ips[1] = ip;

// ITER_MAX
// look_up entry
v.type = INTEGER;
v.index = 2;
look_up["ITER_MAX"] = v;
ip.name = "ITER_MAX";
ip.cvalue = ip.dvalue = 10000;
ip.help = "Maximum number of iterations";
ip.lb = 1;
ip.ub = 1000000000;
ips[2] = ip;

// RESET_FREQ
// look_up entry
v.type = INTEGER;
v.index = 3;
look_up["RESET_FREQ"] = v;
ip.name = "RESET_FREQ";
ip.cvalue = ip.dvalue = 10000;
ip.help = "Required by EXPAND; shouldn't change";
ip.lb = 10000;
ip.ub = 10000;
ips[3] = ip;

// K_ITER
// look_up entry
v.type = INTEGER;
v.index = 4;
look_up["K_ITER"] = v;
ip.name = "K_ITER";
ip.cvalue = ip.dvalue = 1;
ip.help = "Output based on iterations occurs every k_iter iterations";
ip.lb = 1;
ip.ub = 1000000000;
ips[4] = ip;

// L_START
// look_up entry
v.type = INTEGER;
v.index = 5;
look_up["L_START"] = v;
ip.name = "L_START";
ip.cvalue = ip.dvalue = 0;
ip.help = "Amount of output before iteration starts 0-4";
ip.lb = 0;
ip.ub = 4;
ips[5] = ip;

// L_PH1
// look_up entry
v.type = INTEGER;
v.index = 6;
look_up["L_PH1"] = v;
ip.name = "L_PH1";
ip.cvalue = ip.dvalue = 0;
ip.help = "Amount of output at the end of phase 1 and after reinversion 0-4";
ip.lb = 0;
ip.ub = 4;
ips[6] = ip;

// L_END
// look_up entry
v.type = INTEGER;
v.index = 7;
look_up["L_END"] = v;
ip.name = "L_END";
ip.cvalue = ip.dvalue = 3;
ip.help = "Amount of output at the end of the run 0-4";
ip.lb = 0;
ip.ub = 4;
ips[7] = ip;

// L_ITER
// look_up entry
v.type = INTEGER;
v.index = 8;
look_up["L_ITER"] = v;
ip.name = "L_ITER";
ip.cvalue = ip.dvalue = 0;
ip.help = "Amount of output every k_iter iterations 0-4";
ip.lb = 0;
ip.ub = 4;
ips[8] = ip;


// Double parameters

// REFRESH_FACTOR
// look_up entry
v.type = DOUBLE;
v.index = 1;
look_up["REFRESH_FACTOR"] = v;
dp.name = "REFRESH_FACTOR";
dp.cvalue = dp.dvalue = 5.0;
dp.help = "refresh_factor * number of rows gives nominal refresh frequency";
dp.lb = 1.0;
dp.ub = 10000;
dps[1] = dp;

// EPSC
// look_up entry
v.type = DOUBLE;
v.index = 2;
look_up["EPSC"] = v;
dp.name = "EPSC";
dp.cvalue = dp.dvalue = 1.0e-6;
dp.help = "tolerance for column choice";
dp.lb = 1.0e-32;
dp.ub = 1.0e-4;
dps[2] = dp;

// EPSP
// look_up entry
v.type = DOUBLE;
v.index = 3;
look_up["EPSP"] = v;
dp.name = "EPSP";
dp.cvalue = dp.dvalue = 1.0e-11;
dp.help = "tolerance for zero pivot element (t in Gill et al)";
dp.lb = 1.0e-32;
dp.ub = 1.0e-7;
dps[3] = dp;

// EPSI
// look_up entry
v.type = DOUBLE;
v.index = 4;
look_up["EPSI"] = v;
dp.name = "EPSI";
dp.cvalue = dp.dvalue = 1.0e-13;
dp.help = "tolerance for inversion; should be smaller than EPSP";
dp.lb = 1.0e-32;
dp.ub = 1.0e-8;
dps[4] = dp;

// EPSB
// look_up entry
v.type = DOUBLE;
v.index = 5;
look_up["EPSB"] = v;
dp.name = "EPSB";
dp.cvalue = dp.dvalue = 1.0e-6;
dp.help = "tolerance for row choices (b values) should = DELF";
dp.lb = 1.0e-6;
dp.ub = 1.0e-6;
dps[5] = dp;

// EPS
// look_up entry
v.type = DOUBLE;
v.index = 6;
look_up["EPS"] = v;
dp.name = "EPS";
dp.cvalue = dp.dvalue = 1.0e-16;
dp.help = "Machine precision ~ DBL_EPSILON = ~2^-52 for IEEE doubles";
dp.lb = 1.0e-32;
dp.ub = 1.0e-4;
dps[6] = dp;

// DEL0
// look_up entry
v.type = DOUBLE;
v.index = 7;
look_up["DEL0"] = v;
dp.name = "DEL0";
dp.cvalue = dp.dvalue = 0.5e-6;
dp.help = "Initial value for delta (EXPAND)";
dp.lb = 0.5e-6;
dp.ub = 0.5e-6;
dps[7] = dp;

// TAU
// look_up entry
v.type = DOUBLE;
v.index = 8;
look_up["TAU"] = v;
dp.name = "TAU";
dp.cvalue = dp.dvalue = 0.49e-12;
dp.help = "Change in delta each iteration";
dp.lb = 0.49e-12;
dp.ub = 0.49e-12;
dps[8] = dp;

// DELF
// look_up entry
v.type = DOUBLE;
v.index = 9;
look_up["DELF"] = v;
dp.name = "DELF";
dp.cvalue = dp.dvalue = 1.0e-6;
dp.help = "Master feasibility tolerance; should be ~ EPS^(3/8) amd = EPSB";
dp.lb = 1.0e-12;//1.0e-6; 
dp.ub = 1.0e-1;//1.0e-6;
dps[9] = dp;

// Boolean Parameters

// REFRESH_OFF
// look_up entry
v.type = BOOLEAN;
v.index = 1;
look_up["REFRESH_OFF"] = v;
bp.name = "REFRESH_OFF";
bp.cvalue = bp.dvalue = 0;
bp.help = "When 1, on, true, ... turns off reinversion for timing studies";
bps[1] = bp;

// RFLAG
// look_up entry
v.type = BOOLEAN;
v.index = 2;
look_up["RFLAG"] = v;
bp.name = "RFLAG";
bp.cvalue = bp.dvalue = 0;
bp.help = "Extra debuggin information when non-zero, true, on, ....";
bps[2] = bp;

// MAX
// look_up entry
v.type = BOOLEAN;
v.index = 3;
look_up["MAX"] = v;
bp.name = "MAX";
bp.cvalue = bp.dvalue = 0;
bp.help = "If maximizing rather than minimizing";
bps[3] = bp;

// "Action" Parameters

// QUIT
// look_up entry
v.type = ACTION;
v.index = 1;
look_up["QUIT"] = v;
ap.name = "QUIT";
ap.help = "Leaves interactive mode";
aps[1] = ap;

// HELP
// look_up entry
v.type = ACTION;
v.index = 2;
look_up["HELP"] = v;
ap.name = "HELP";
ap.help = "Used to get help, and values of defaults and bounds.";
aps[2] = ap;

// SHOW
// Show license information
v.type = ACTION;
v.index = 3;
look_up["SHOW"] = v;
ap.name = "SHOW";
ap.help = "Shows license information: show c->copyright, show w->no warrantee.";
aps[3] = ap;


return;
}

int UpDate_p(std::string pname, std::string vstring)
{

p_index v;
ip_attributes ip;
dp_attributes dp;
bp_attributes bp;
ap_attributes ap;

  // Look up type
  std::map<std::string, p_index>::iterator it = look_up.find(pname);
  if (it != look_up.end()) 
    v = it->second;
  else
    {
    std::cerr << "Parameter name " << pname << " not defined." << std::endl;
    return 5;
    }
  // Branch on type
  long iv;
  double dv;

  char c;
  std::string av, s38d(38,'-');
  switch(v.type)
    {
    case INTEGER:
      ip = ips[v.index];
      std::cout << s38d << std::endl;
      std::cout << ip.help << std::endl ;
      iv = strtol(vstring.c_str(), NULL, 10);
      // Is value an integer? Is value within bounds?
      if((errno != ERANGE) && (iv <= ip.ub) && (iv >= ip.lb)) 
	{
	  // Yes, change value.
	  std::cout << "Parameter " << pname << " has value changed from "  << ip.cvalue;
	  ips[v.index].cvalue = iv;
	  std::cout << " to " << ips[v.index].cvalue << std::endl;
	  std::cout << s38d << std::endl;
	}
      else
	{
	  // No, leave value unchanged.
	  std::cerr << "Parameter " << pname << " has value " << vstring << " outside of range ["
		    << ip.lb << ", " << ip.ub << "]." << std::endl;
	  return INTEGER;
	}
      break;
      //
    case DOUBLE:
      dp = dps[v.index];
      std::cout << s38d << std::endl;
      std::cout << dp.help << std::endl ;
      dv = strtod(vstring.c_str(),NULL);
      // Is value a double? Is value within bounds?
      if((errno != ERANGE) && (dv <= dp.ub) && (dv >= dp.lb)) 
	{
	  // Yes, change value.
	  std::cout << "Parameter " << pname << " has value changed from "  << dp.cvalue;
	  dps[v.index].cvalue = dv;
	  std::cout << " to " << dps[v.index].cvalue << std::endl;
	  std::cout << s38d << std::endl;
	}
      else
	{
	  // No, leave value unchanged.
	  std::cerr << "Parameter " << pname << " has value " << vstring << " outside of range ["
		    << dp.lb << ", " << dp.ub << "]." << std::endl;
	  return DOUBLE;
	}
      break;
      //
    case BOOLEAN:
      bp = bps[v.index];
      std::cout << s38d << std::endl;
      std::cout << pname << ", " <<bp.dvalue <<", " <<std::endl;
      std::cout << bp.help << std::endl ;
      // Is value a boolean?
      c = toupper(vstring[0]);
      if(c=='Y'||c=='T'||c=='1') 
	{
	  // Yes, change value to 1.
	  std::cout << "Parameter " << pname << " has value changed from "  << bp.cvalue;
	  bps[v.index].cvalue = 1;
	  std::cout << " to " << bps[v.index].cvalue << std::endl;
	  std::cout << s38d << std::endl;
	}
      else
	{ 
	  if(c=='N'||c=='F'||c=='0')
	    {
	  // Yes, change value to 0.
	  std::cout << "Parameter " << pname << " has value changed from "  << bp.cvalue;
	  bps[v.index].cvalue = 0;
	  std::cout << " to " << bps[v.index].cvalue << std::endl;
	    }
	  else
	    {
	      // Unknown value, leave value unchanged.
	      std::cerr << "Parameter " << pname << " has unknown value " << vstring  << std::endl;
	      return BOOLEAN;
	    }
	}
      break;
      //
    case ACTION:
      // For HELP provide it.
      // If there is no value, return -1 to recycle second arg
      if(strcmp(pname.c_str(), "HELP")==0)
	{
	  // No value for help; new parameter
	  if(vstring[0] == '-')
	    {
	      // New parameter, give short help and recycle
	      std::cout << s38d << std::endl;
	      ShortHelp();
	      return -1;
	    }
	  // Now see if the second string is a parameter name
	  std::string s = vstring;
	  transform(s.begin(),s.end(),s.begin(), toupper);
	  std::map<std::string, p_index>::iterator it = look_up.find(s);
	  if (it != look_up.end())
	    {
	      p_index w = it->second;
	    // Give info on parameter
	      std::cout << s38d << std::endl;
	    KeyWordHelp(w.type, w.index);
	    return 0;
	    }
	  else
	    {
	      if(strcmp(s.c_str(),"ALL")==0)
		{
		  // Extended help for all parameters
		  std::cout << s38d << std::endl;
		  LongHelp();
		  return 0;
		}
	      else
		{
		  // Name not recognized; assume file name and recycle
		  // Give short help
		  ShortHelp();
		  return -1;
		}
	    }
	}
      //
      // Show license info
      if(strcmp(pname.c_str(), "SHOW")==0)
	{
	  // Make value upper case string
	  transform(vstring.begin(),vstring.end(),vstring.begin(), toupper);
	  if(strcmp(vstring.c_str(), "C") == 0)
	    {
	      // print copyright info
	      std::cout<< "This program is free software; you can " << std::endl
		       << "redistribute it and/or modify it under the terms " << std::endl
		       << "of the GNU General Public License as published by" << std::endl
		       << "the Free Software Foundation; either version 2 of " << std::endl
		       << "the License, or (at your option) any later version." << std::endl
		       << std::endl
		       << "You should have received a copy of the GNU General " << std::endl
		       << "Public License along with this program " << std::endl
		       << "(License.txt); if not, write to the Free Software" << std::endl
		       << "Foundation, Inc., 51 Franklin Street, Fifth Floor, " << std::endl
		       << "Boston, MA  02110-1301, USA" << std::endl
		       << std::endl
		       << "If you have any questions or comments, you can " << std::endl
		       << "reach us at Richard Van Slyke, Polytechnic " << std::endl
		       << "University, 6 Metrotech Center, Brooklyn, NY, " << std::endl
		       << "11217,  USA, or by e-mail: rvslyke@poly.edu." << std::endl;
	    }
	  else if(strcmp(vstring.c_str(), "W") == 0) 
	    {
	      // print warantee disclaimer
	      std::cout<< "This program is distributed in the hope that it " << std::endl
		       << "will be useful, but WITHOUT ANY WARRANTY; without " << std::endl
		       << "even the implied warranty of MERCHANTABILITY or " << std::endl
		       << "FITNESS FOR A PARTICULAR PURPOSE.  See the GNU " << std::endl
		       << "General Public License for more details." << std::endl
		       << std::endl
		       << "You should have received a copy of the GNU General " << std::endl
		       << "Public License along with this program " << std::endl
		       << "(License.txt); if not, write to the Free Software" << std::endl
		       << "Foundation, Inc., 51 Franklin Street, Fifth Floor, " << std::endl
		       << "Boston, MA  02110-1301, USA" << std::endl
		       << std::endl
		       << "If you have any questions or comments, you can " << std::endl
		       << "reach us at Richard Van Slyke, Polytechnic " << std::endl
		       << "University, 6 Metrotech Center, Brooklyn, NY, " << std::endl
		       << "11217,  USA, or by e-mail: rvslyke@poly.edu." << std::endl;
	    }
	  else
	    {
	      std::cerr << "Unknown argument to SHOW" << std::endl;
	      return ACTION;
	    }
      break;
	}
    }
  return 0;
}
int WhatIs_ptype(std::string pname)
{
  p_index v;

  // Look up type
std::map<std::string, p_index>::iterator  it = look_up.find(pname);
  if (it != look_up.end()) 
    v = it->second;
  else
    {
    std::cerr << "Parameter name " << pname << "Not defined." << std::endl;
    return 0;
    }
  return v.type;
}


void ShortHelp()
{
  std::cout << "Short Help" << std::endl;
  std::cout << "For more information on parameters, try \"retroLP -help all\"" << std::endl;
  std::cout << "For more on parameter \"keyword\", try \"retroLP -help keyword\"" << std::endl;
  std::cout << std::endl;
  std::cout << "The parameters and actions are:" << std::endl;
  std::cout << "Integer parameters: " << std::endl;

  for(int i=1; i <= ni; i++)
  {
    std::cout << ips[i].name << ": " << ips[i].help << std::endl;
  }
std::cout    << "Float parameters: " << std::endl;
  for(int i=1; i <= nd; i++)
  {
    std::cout << dps[i].name << ": " << dps[i].help << std::endl;
  }
std::cout    << "Boolean parameters: " << std::endl;
  for(int i=1; i <= nb; i++)
  {
    std::cout << bps[i].name << ": " << bps[i].help << std::endl;
  }
std::cout    << "Actions: " << std::endl;
  for(int i=1; i <= na; i++)
  {
    std::cout << aps[i].name << ": " << aps[i].help << std::endl;
  }
  std::cout << "For more information on parameters, try \"retroLP -help all\"" << std::endl;
  std::cout << "For more on parameter \"keyword\", try \"retroLP -help keyword\"" << std::endl;
  return;
}

void LongHelp()
{
  std::cout << "Long Help" << std::endl;
  std::cout << "The parameters and actions are:" << std::endl;
  std::cout << std::endl;
  std::cout << "Integer parameters: " << std::endl;

  for(int i=1; i <= ni; i++)
  {
    std::cout << ips[i].name << ": " << ips[i].help << std::endl;
    std::cout << "Current value: " << ips[i].cvalue << " Default value: "<< ips[i].dvalue 
	      <<  ". Bounds[" << ips[i].lb << ", " <<ips[i].ub << "]." << std::endl;
  }
std::cout    << "Float parameters: " << std::endl;
  for(int i=1; i <= nd; i++)
  {
    std::cout << dps[i].name << ": " << dps[i].help << std::endl;
    std::cout << "Current value: " << dps[i].cvalue << " Default value: "<< dps[i].dvalue 
	      <<  ". Bounds[" << dps[i].lb << ", " <<dps[i].ub << "]." << std::endl;
  }
std::cout    << "Boolean parameters: " << std::endl;
  for(int i=1; i <= nb; i++)
  {
    std::cout << bps[i].name << ": " << bps[i].help << std::endl;
    std::cout << "Current value: " << bps[i].cvalue << " Default value: "<< bps[i].dvalue << std::endl;
}
std::cout    << "Actions: " << std::endl;
  for(int i=1; i <= na; i++)
  {
    std::cout << aps[i].name << ": " << aps[i].help << std::endl;
  }
  return;
}

int KeyWordHelp(int t, int i)
{
  switch(t)
    {
    case INTEGER:
      std::cout << ips[i].name << ": " << ips[i].help << std::endl;
      std::cout << "Current value: " << ips[i].cvalue << ", Default value: "<< ips[i].dvalue 
		<<  ". Bounds[" << ips[i].lb << ", " <<ips[i].ub << "]." << std::endl;
      break;
    case DOUBLE:
      std::cout << dps[i].name << ": " << dps[i].help << std::endl;
      std::cout << "Current value: " << dps[i].cvalue << ", Default value: "<< dps[i].dvalue 
		<<  ". Bounds[" << dps[i].lb << ", " <<dps[i].ub << "]." << std::endl;
	    break;
    case BOOLEAN:
      std::cout << bps[i].name << ": " << bps[i].help << std::endl;
      std::cout << "Current value: " << bps[i].cvalue << ", Default value: "<< bps[i].dvalue << std::endl;
	    break;
    case ACTION:
      std::cout << "Key word help for: " ;
      std::cout << aps[i].name << std::endl;
      std::cout << aps[i].name << ": " << aps[i].help << std::endl;
	    break;
    }
  return 0;
}

/************************************************************************
* retroLP solves linear programs using the simplex method.             *
* Copyright (C) 2001, 2003, 2005 Gavriel Yarmish & Richard Van Slyke.  *
*                                                                      *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or    *
* (at your option) any later version.                                  *
*                                                                      *
* This program is distributed in the hope that it will be useful,      *
* but WITHOUT ANY WARRANTY; without even the implied warranty of       *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
* GNU General Public License for more details.                         *
*                                                                      *
* You should have received a copy of the GNU General Public License    *  
* along with this program; if not, write to the Free Software          *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,           *
*                                                                      *
************************************************************************/
