/*-------------------------------------------------------------------------------------*/
/*  NOMAD - Nonsmooth Optimization by Mesh Adaptive Direct search - version 3.0.0      */
/*                                                                                     */
/*  Copyright (C) 2001-2008  Mark Abramson        - the Boeing Company, Seattle        */
/*                           Charles Audet        - Ecole Polytechnique, Montreal      */
/*                           Gilles Couture       - Ecole Polytechnique, Montreal      */
/*                           John Dennis          - Rice University, Houston           */
/*                           Sebastien Le Digabel - Ecole Polytechnique, Montreal      */
/*                                                                                     */
/*  funded in part by AFOSR and Exxon Mobil                                            */
/*                                                                                     */
/*  Author: Sebastien Le Digabel                                                       */
/*                                                                                     */
/*  Contact information:                                                               */
/*    Ecole Polytechnique de Montreal - GERAD                                          */
/*    C.P. 6079, Succ. Centre-ville, Montreal (Quebec) H3C 3A7 Canada                  */
/*    e-mail: Sebastien.Le.Digabel@gerad.ca                                            */
/*    phone : 1-514-340-6053 #6673                                                     */
/*    fax   : 1-514-340-5665                                                           */
/*                                                                                     */
/*  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 3 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, see <http://www.gnu.org/licenses/>.                               */
/*                                                                                     */
/*  You can find information on the NOMAD software at www.gerad.ca/nomad               */
/*-------------------------------------------------------------------------------------*/
#include "utils.hpp"
using namespace std;

/*---------------------------------------------------------------*/
/*               construct the first n prime numbers             */
/*---------------------------------------------------------------*/
void construct_primes ( int n , int * primes ) {
  bool is_prime;
  int i = 0 , k = 2 , j;
  double kk;
  while ( true ) {
    is_prime = true;
    kk = sqrt ( static_cast<double>(k) );
    for ( j = 2 ; j <= kk ; ++j )
      if ( k%j==0 ) {
	is_prime = false;
	break;
      }
    if ( is_prime ) {
      primes[i++] = k;
      if ( i==n )
	break;
    }
    ++k;
  }
}

/*--------------------------------------------*/
/*  decompose a string (sentence) into words  */
/*--------------------------------------------*/
void NOMAD::get_words ( const string & sentence , list<string> & words ) {
  string        s;
  istringstream in(sentence);
  while (true) {
    in >> s;
    if (in.fail())
      break;
    words.push_back (s);
  }
}

/*-----------------------------------------------------------------*/
/*              check if a file exists and is executable           */
/*-----------------------------------------------------------------*/
bool NOMAD::check_exe_file ( const string & file_name ) {
#ifdef _MSC_VER
  // with visual C++, we can only check if the file is readable:
  return ( _access ( file_name.c_str() , 4 ) == 0 );
#else
  return ( access ( file_name.c_str() , X_OK ) == 0 );
#endif
}

/*-----------------------------------------------------------------*/
/*              check if a file exists and is readable             */
/*-----------------------------------------------------------------*/
bool NOMAD::check_read_file ( const string & file_name ) {
#ifdef _MSC_VER
  return ( _access ( file_name.c_str() , 4 ) == 0 );
#else
  return ( access ( file_name.c_str() , R_OK ) == 0 );
#endif
}

/*-----------------------------------------------------------------*/
/*                         NOMAD::itos                             */
/*-----------------------------------------------------------------*/
string NOMAD::itos ( int i ) {
  ostringstream oss;
  oss << i;
  return oss.str();
}

/*-----------------------------------------------------------------*/
/*                         NOMAD::toupper                          */
/*-----------------------------------------------------------------*/
void NOMAD::toupper ( string & s ) {
  size_t ns = s.size();
  for ( size_t i = 0 ; i < ns ; ++i )
    s[i] = std::toupper(s[i]);
}

/*-----------------------------------------------------------------*/
/*                             NOMAD::atoi                         */
/*-----------------------------------------------------------------*/
bool NOMAD::atoi ( const string & s , int & i ) {
  i = -1;
  if ( s.empty() )
    return false;

  size_t n = s.size();

  if ( s[0] == '-' ) {
    if ( n > 1 && s[1] == '-' )
      return false;
    string ss = s;
    ss.erase(ss.begin());
    if ( NOMAD::atoi ( ss , i ) ) {
      i = -i;
      return true;
    }
    return false;
  }

  for ( size_t k = 0 ; k < n ; ++k )
    if ( !isdigit(s[k]) )
      return false;
  i = std::atoi(s.c_str());
  return true;
}

bool NOMAD::atoi ( char c , int & i ) {
  string s = "-";
  s[0] = c;
  return NOMAD::atoi(s,i);
}

/*-----------------------------------------------------------------*/
/*                      to display a success_type                  */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , success_type st ) {
  switch ( st ) {
  case _FULL_SUCCESS_:
    out << "dominating";
    break;
  case _PARTIAL_SUCCESS_:
    out << "improving";
    break;
  case _UNSUCCESSFUL_:
    out << "unsuccessful";
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*                 to display a vector of bb-input type's          */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , const vector<bb_input_type> & bbits ) {
  if ( bbits.empty() )
    return out;
  size_t n = bbits.size()-1;
  for ( size_t k = 0 ; k < n ; ++k )
    out << setw(8) << bbits[k] << " ";
  out << setw(8) << bbits[n];
  return out;
}

/*-----------------------------------------------------------------*/
/*                     to display a bb-input type                  */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , bb_input_type bi ) {
  switch ( bi ) {
  case _CONTINUOUS_:
    out << "cont(R)";
    break;
  case _CATEGORICAL_:
    out << "cat(C)";
    break;
  case _INTEGER_:
    out << "int(I)";
    break;
  case _BINARY_:
    out << "bin(B)";
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*                     to display a search type                    */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , search_type st ) {
  switch ( st ) {
  case _POLL_:
    out << "poll";
    break;
  case _SEARCH_:
    out << "search";
    break;
  case _CACHE_SEARCH_:
    out << "cache search";
    break;
  case _MADS_SEARCH_:
    out << "MADS speculative search";
    break;
  case _LH_SEARCH_:
    out << "LH search";
    break;
  case _VNS_SEARCH_:
    out << "VNS search";
    break;
  case _X0_:
    out << "x0 evaluation";
    break;
  case _UNDEFINED_SEARCH_:
    out << "undefined";
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*                       to display a stop reason                  */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , stop_type sr ) {
  switch ( sr ) {
  case _DELTA_M_MIN_REACHED_:
    out << "min mesh size reached";
    break;
  case _DELTA_P_MIN_REACHED_:
    out << "min poll size reached";
    break;
  case _L_MAX_REACHED_:
    out << "max mesh index reached";
    break;
  case _L_LIMITS_REACHED_:
    out << "mesh index limits (+/- " << L_LIMITS << " reached)";
    break;
  case _MAX_TIME_REACHED_:
    out << "max time reached";
    break;
  case _MAX_BB_EVAL_REACHED_:
    out << "max number of black-box evaluations reached";
    break;
  case _MAX_SGTE_EVAL_REACHED_:
    out << "max number of surrogate evaluations reached";
    break;
  case _MAX_EVAL_REACHED_:
    out << "max number of evaluations (black-box eval. + cache hits) reached";
    break;
  case _MAX_SIM_BB_EVAL_REACHED_:
    out << "max number of simulated black-box evaluations (black-box eval. + initial cache hits) reached";
    break;
  case _MAX_ITER_REACHED_:
    out << "max number of iterations reached";
    break;
  case _MIN_EPS_REACHED_:
    out << "min precision (eps) reached";
    break;
  case _MIN_PROGRESS_REACHED_:
    out << "min progress condition reached";
    break;
  case _F_TARGET_REACHED_:
    out << "objective target reached";
    break;
  case _PHASE_1_FAIL_:
    out << "phase one failed";
    break;
  case _STAT_SUM_TARGET_REACHED_:
    out << "stat sum target reached";
    break;
  case _X0_EVAL_:
    out << "problem with starting point evaluation";
    break;
  case _TRIAL_PT_CONSTRUCTION_:
    out << "a null direction was generated (mesh too thin ?)";
    break;
  case _CTRL_C_:
    out << "force quit by pressing ctrl-c";
    break;
  case _UNKNOWN_:
  case _NO_STOP_:
    out << "unknown";
    break;
  case _ERROR_:
    out << "error";
    break;
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*          if a bb_output_type corresponds to a constraint        */
/*-----------------------------------------------------------------*/
bool bbot_is_constraint ( bb_output_type bbot ) {
  return ( bbot==_EB_ || bbot==_PB_ || bbot==_PEB_P_ || bbot==_PEB_E_ || bbot==_F_ );
}

/*-----------------------------------------------------------------*/
/*                    to display a bb_output_type                  */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , bb_output_type bbt ) {
  switch ( bbt ) {
  case _OBJ_:
    out << "OBJ";
    break;
  case _EB_:
    out << "EB";
    break;
  case _PB_:
    out << "PB";
    break;
  case _PEB_P_:
    out << "PEB(P)";
    break;
  case _PEB_E_:
    out << "PEB(E)";
    break;
  case _F_:
    out << "F";
    break;
  case _STAT_AVG_:
    out << "STAT_AVG";
    break;
  case _STAT_SUM_:
    out << "STAT_SUM";
    break;
  case _NOTHING_:
    out << "-";
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*                       to display a hnorm_type                   */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , hnorm_type hnorm ) {
  switch ( hnorm ) {
  case _L1_:
    out << "L1";
    break;
  case _L2_:
    out << "L2";
    break;
  case _LINF_:
    out << "Linf";
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*                      to display a direction_type                */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , direction_type dt ) {
  switch ( dt ) {
  case _ORTHO_1_:
    out << "Ortho-MADS 1";
    break;
  case _ORTHO_2_:
    out << "Ortho-MADS 2";
    break;
  case _ORTHO_2N_:
    out << "Ortho-MADS 2n";
    break;
  case _LT_1_:
    out << "LT-MADS 1";
    break;
  case _LT_2_:
    out << "LT-MADS 2";
    break; 
  case _LT_2N_:
    out << "LT-MADS 2n";
    break;
  case _LT_NP1_:
    out << "LT-MADS n+1";
    break;
  case _GPS_BINARY_:
    out << "GPS n, binary";
    break;
  case _GPS_2N_STATIC_:
    out << "GPS 2n, static";
    break;
  case _GPS_2N_RAND_:
    out << "GPS 2n, random";
    break;
  case _GPS_NP1_STATIC_UNIFORM_:
    out << "GPS n+1, static, uniform angles";
    break;
  case _GPS_NP1_STATIC_:
    out << "GPS n+1, static";
    break;
  case _GPS_NP1_RAND_UNIFORM_:
    out << "GPS n+1, random, uniform angles";
    break;
  case _GPS_NP1_RAND_:
    out << "GPS n+1, random";
    break;
  case _NO_DIRECTION_:
    out << "none";
    break;
  case _UNDEFINED_DIRECTION_:
    out << "undefined";
    break;
  }
  return out;
}

/*---------------------------------------------------------*/
/*             if a direction is a MADS direction          */
/*---------------------------------------------------------*/
bool dir_is_mads ( direction_type dt ) {
  return ( dt == _ORTHO_1_  ||
	   dt == _ORTHO_2_  ||
	   dt == _ORTHO_2N_ ||
	   dt == _LT_1_     ||
	   dt == _LT_2_     ||
	   dt == _LT_2N_    ||
	   dt == _LT_NP1_      );
}

/*---------------------------------------------------------*/
/*             if a direction is a GPS direction           */
/*---------------------------------------------------------*/
bool dir_is_gps ( direction_type dt ) {
  return ( dt == _GPS_BINARY_             ||
	   dt == _GPS_2N_STATIC_          ||
	   dt == _GPS_2N_RAND_            ||
	   dt == _GPS_NP1_STATIC_UNIFORM_ ||
	   dt == _GPS_NP1_STATIC_         ||
	   dt == _GPS_NP1_RAND_UNIFORM_   ||
	   dt == _GPS_NP1_RAND_              );
}

/*---------------------------------------------------------*/
/*           if a direction is a LT-MADS direction         */
/*---------------------------------------------------------*/
bool dir_is_ltmads ( direction_type dt ) {
  return ( dt == _LT_1_     ||
	   dt == _LT_2_     ||
	   dt == _LT_2N_    ||
	   dt == _LT_NP1_      );
}

/*---------------------------------------------------------*/
/*         if a direction is a Ortho-MADS direction        */
/*---------------------------------------------------------*/
bool dir_is_orthomads ( direction_type dt ) {
  return ( dt == _ORTHO_1_  ||
	   dt == _ORTHO_2_  ||
	   dt == _ORTHO_2N_    );
}

/*---------------------------------------------------------*/
/*            if a direction is a random direction         */
/*---------------------------------------------------------*/
bool dir_is_random ( direction_type dt ) {
  return ( dt == _GPS_NP1_RAND_         ||
	   dt == _GPS_NP1_RAND_UNIFORM_ ||
	   dt == _GPS_2N_RAND_             );
}

/*-----------------------------------------------------------------*/
/*         interpret a list of strings as a direction type         */
/*-----------------------------------------------------------------*/
bool interpret_direction_type ( const list<string> & ls , direction_type & dt ) {
  
  dt = _UNDEFINED_DIRECTION_;

  if ( ls.empty() || ls.size() > 4 )
    return false;

  list<string>::const_iterator it = ls.begin() , end = ls.end();
  string                       s  = *it;
  NOMAD::toupper(s);

  // no direction:
  if ( s == "NONE" ) {
    dt = _NO_DIRECTION_;
    return true;
  }

  // Ortho-MADS with 1, 2 or 2n directions:
  if ( s == "ORTHO" ) {
    ++it;
    if ( it == end ) {
      dt = _ORTHO_2N_;
      return true;
    }
    if ( *it == "1" ) {
      dt = _ORTHO_1_;
      return true;
    }
    if ( *it == "2" ) {
      dt = _ORTHO_2_;
      return true;
    }
    s = *it;
    NOMAD::toupper(s);
    if ( s == "2N" ) {
      dt = _ORTHO_2N_;
      return true;
    }
    return false;
  }

  // LT-MADS with 1, 2 or 2n directions:
  if ( s == "LT" ) {
    ++it;
    if ( it == end ) {
      dt = _LT_2N_;
      return true;
    }
    if ( *it == "1" ) {
      dt = _LT_1_;
      return true;
    }
    if ( *it == "2" ) {
      dt = _LT_2_;
      return true;
    }
    s = *it;
    NOMAD::toupper(s);
    if ( s == "N+1" ) {
      dt = _LT_NP1_;
      return true;
    }
    if ( s == "2N" ) {
      dt = _LT_2N_;
      return true;
    }
    return false;
  }

  // GPS:
  if ( s == "GPS" ) {
    ++it;
    if ( it == end ) {
      dt = _GPS_2N_STATIC_;
      return true;
    }
    s = *it;
    NOMAD::toupper(s);

    // GPS for binary variables:
    if ( s == "BINARY" || s == "BIN" ) {
      dt = _GPS_BINARY_;
      return true;
    }

    // GPS, n+1 directions:
    if ( s == "N+1" ) {
      ++it;
      if ( it == end ) {
	dt = _GPS_NP1_STATIC_;
	return true;
      }
      s = *it;
      NOMAD::toupper(s);

      // GPS, n+1, static:
      if ( s == "STATIC" ) {
	++it;
	if ( it == end ) {
	  dt = _GPS_NP1_STATIC_;
	  return true;
	}
	s = *it;
	NOMAD::toupper(s);
	if ( s == "UNIFORM" ) {
	  dt = _GPS_NP1_STATIC_UNIFORM_;
	  return true;
	}
	return false;
      }

      // GPS, n+1, random:
      if ( s == "RAND" || s == "RANDOM" ) {
	++it;
	if ( it == end ) {
	  dt = _GPS_NP1_RAND_;
	  return true;
	}
	s = *it;
	NOMAD::toupper(s);
	if ( s == "UNIFORM" ) {
	  dt = _GPS_NP1_RAND_UNIFORM_;
	  return true;
	}
	return false;
      }
      return false;
    }
    
    // 2n directions:
    if ( s == "2N" ) {
      ++it;
      if ( it == end ) {
	dt = _GPS_2N_STATIC_;
	return true;
      }
      s = *it;
      NOMAD::toupper(s);
      if ( s == "STATIC" ) {
	dt = _GPS_2N_STATIC_;
	return true;
      }
      if ( s == "RAND" || s == "RANDOM" ) {
	dt = _GPS_2N_RAND_;
	return true;
      }
      return false;
    }
    return false;
  }
  return false;
}

/*-----------------------------------------------------------------*/
/*                    to display a check_failed_type               */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , check_failed_type cf ) {
  switch ( cf ) {
  case _OK_:
    out << "ok";
    break;
  case _LB_FAIL_:
    out << "lower bound";
    break;
  case _UB_FAIL_:
    out << "upper bound";
    break;
  case _FIX_VAR_FAIL_:
    out << "fixed variable";
    break;
  case _BIN_FAIL_:
    out << "binary variable";
    break;
  case _CAT_FAIL_:
    out << "caterogical variable";
    break;
  case _INT_FAIL_:
    out << "integer variable";
    break;
  }
  return out;
}

/*-----------------------------------------------------------------*/
/*                    to display a size in memory                  */
/*-----------------------------------------------------------------*/
void display_sizeof ( int size , ostream & out ) {
  if ( size < 1024 )
    out << size << " B";
  else if ( size < 1048576 )
    out << int ( 10*size / 1024.0    ) / 10.0 << " KB";
  else
    out << int ( 10*size / 1048576.0 ) / 10.0 << " MB";
}

/*-----------------------------------------------------------------*/
/*                      to display a display degree                */
/*-----------------------------------------------------------------*/
void display_display_degree ( int dd , ostream & out ) {
  switch ( dd ) {
  case 0:
    out << "no";
    break;
  case 1:
    out << "minimal";
    break;
  case 2:
    out << "normal";
    break;
  case 3:
    out << "detailed";
    break;
  case 4:
    out << "full / debug";
  }
}

/*-------------------------------------------------------------------*/
/*            get the display_stats_type from a string               */
/*-------------------------------------------------------------------*/
display_stats_type get_display_stats_type ( const string & s ) {
  int    idst;
  string ss = s , keyword;
  NOMAD::toupper(ss);
  display_stats_type dst = _DS_OBJ_;
  while ( dst < _DS_UNDEFINED_ ) {
    keyword = get_display_stats_keyword (dst);
    if ( keyword == ss )
      return dst;    
    idst = dst;
    ++idst;
    dst = static_cast<display_stats_type> (idst);
  }
  return _DS_UNDEFINED_;
}

/*-------------------------------------------------------------------*/
/*     to get the keyword associated with a display_stats_type       */
/*  (used in Parameters::read() or Parameters::set_DISPLAY_STATS())  */
/*-------------------------------------------------------------------*/
string get_display_stats_keyword ( display_stats_type dst ) {
  string s;
  switch (dst) {
  case _DS_OBJ_:
    s = "OBJ";
    break;
  case _DS_EVAL_:
    s = "EVAL";
    break;
  case _DS_SIM_BBE_:
    s = "SIM_BBE";
    break;
  case _DS_BBE_:
    s = "BBE";
    break;
  case _DS_BBO_:
    s = "BBO";
    break;
  case _DS_SOL_:
    s = "SOL";
    break;
  case _DS_TIME_:
    s = "TIME";
    break;
  case _DS_STAT_SUM_:
    s = "STAT_SUM";
    break;
  case _DS_STAT_AVG_:
    s = "STAT_AVG";
    break;
  case _DS_UNDEFINED_:
    s = "undefined";
    break;
  }
  return s;
}

/*-----------------------------------------------------------------*/
/*                  to display a display_stats_type                */
/*-----------------------------------------------------------------*/
ostream & operator << ( ostream & out , display_stats_type dst ) {
  out << get_display_stats_keyword(dst);
  return out;
}

/*-----------------------------------------------------------------*/
/*                convert a string into a bb_input_type            */
/*-----------------------------------------------------------------*/
bool string_to_bb_input_type ( const string & ss , bb_input_type & bbit ) {
  string s = ss;
  NOMAD::toupper (s);
  if ( s=="R" || s=="REAL" ) {
    bbit = _CONTINUOUS_;
    return true;
  }
  if ( s=="C" || s=="CAT" ) {
    bbit = _CATEGORICAL_;
    return true;
  }
  if ( s=="B" || s=="BIN" ) {
    bbit = _BINARY_;
    return true;
  }
  if ( s=="I" || s=="INT" ) {
    bbit = _INTEGER_;
    return true;
  }
  return false;
}

/*----------------------------------------------------------------------*/
/*         convert a string in {"YES","NO","Y","N"} to a bool           */
/*         value of return: -1: error                                   */
/*                           0: bool=false                              */
/*                           1: bool=true                               */
/*----------------------------------------------------------------------*/
int string_to_bool ( const string & ss ) {
  string s = ss;
  NOMAD::toupper (s);
  if ( s=="Y" || s=="YES" )
    return 1;
  if (s=="N" || s=="NO" )
    return 0;
  return -1;
}

/*----------------------------------------------------------------------*/
/*                  display a bool with format yes/no                   */
/*  (better than 'out.setf(ios::boolalpha)' that displays true/false)   */
/*----------------------------------------------------------------------*/
void display_yes_or_no ( bool b , ostream & out ) {
  out << ( (b) ? "yes" : "no" );
}

/*---------------------------------------------------*/
/*  convert a string 'i-j' to the integers i and j   */
/*---------------------------------------------------*/
bool string_to_index_range ( const string & s           ,
			     int          & i           ,
			     int          & j           ,
			     int          * n           ,
			     bool           check_order   ) {

  if ( s.empty() )
    return false;

  if ( s == "*" ) {
    if (!n)
      return false;
    i = 0;
    j = *n-1;
    return true;
  }

  if ( s[0] == '-' ) {

    size_t ns = s.size();
    if ( ns > 1 && s[1] == '-' )
      return false;

    string ss = s;
    ss.erase(ss.begin());
    
    if ( string_to_index_range ( ss , i , j , n , false ) ) {
      i = -i;
      return true;
    }
    return false;
  }

  istringstream in (s);
  
  string s1;

  getline ( in , s1 , '-' );

  if (in.fail())
    return false;

  size_t k , n1 = s1.size();

  if ( n1 >= s.size() - 1 ) {
    for ( k = 0 ; k < n1 ; ++k )
      if (!isdigit(s1[k]))
	return false;
    if ( !NOMAD::atoi(s1, i) )
      return false;
    if ( n1 == s.size() ) {
      j = i;
      return true;
    }
    if (n) {
      j = *n-1;
      return true;
    }
    return false;
  }


  string s2;
  getline (in, s2);

  if (in.fail())
    return false;

  size_t n2 = s2.size();
  for ( k = 0 ; k < n2 ; ++k )
    if ( !isdigit(s2[k]) )
      return false;

  if ( !NOMAD::atoi(s1, i) || !NOMAD::atoi(s2, j) )
    return false;

  return !check_order || i <= j;
}
