/*-------------------------------------------------------------------------------------*/
/*  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 "Double.hpp"
using namespace std;

/*-----------------------------------*/
/*   static members initialization   */
/*-----------------------------------*/
double NOMAD::Double::_epsilon         = DEFAULT_EPSILON;
#ifdef DEBUG
int    NOMAD::Double::_cardinality     = 0;
int    NOMAD::Double::_max_cardinality = 0;
#endif

/*-----------------------------------------------*/
/*                  constructor 1                */
/*-----------------------------------------------*/
NOMAD::Double::Double ( void ) : _value(0.0) , _defined(false) {
#ifdef DEBUG
  ++NOMAD::Double::_cardinality;
  if ( NOMAD::Double::_cardinality > NOMAD::Double::_max_cardinality )
    ++NOMAD::Double::_max_cardinality;
#endif
}

/*-----------------------------------------------*/
/*                  constructor 2                */
/*-----------------------------------------------*/
NOMAD::Double::Double ( double v ) : _value   (v   ) ,
				     _defined (true)   {
#ifdef DEBUG
  ++NOMAD::Double::_cardinality;
  if (NOMAD::Double::_cardinality > NOMAD::Double::_max_cardinality)
    ++NOMAD::Double::_max_cardinality;
#endif
}

/*-----------------------------------------------*/
/*                  constructor 3                */
/*-----------------------------------------------*/
NOMAD::Double::Double ( const Double & d ) : _value   (d._value  ) ,
					     _defined (d._defined)   {
#ifdef DEBUG
  ++NOMAD::Double::_cardinality;
  if (NOMAD::Double::_cardinality > NOMAD::Double::_max_cardinality)
    ++NOMAD::Double::_max_cardinality;
#endif
}

/*-----------------------------------------------*/
/*                    destructor                 */
/*-----------------------------------------------*/
NOMAD::Double::~Double (void)  {
#ifdef DEBUG
  _value   = 0.0;
  _defined = false;
  --NOMAD::Double::_cardinality;
#endif
}

/*-----------------------------------------------*/
/*                 set epsilon                   */
/*-----------------------------------------------*/
void NOMAD::Double::set_epsilon ( double eps )  {
  if ( eps <= 0.0 )
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
			     "NOMAD::Double::set_epsilon(): invalid epsilon" );
  Double::_epsilon = eps;
}

/*-----------------------------------------------*/
/*                  get the value                */
/*-----------------------------------------------*/
double NOMAD::Double::value (void) const {
  if (!_defined)
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double::value(): value not defined" );
  return _value;
}

/*------------------------------------------*/
/*                    input                 */
/*------------------------------------------*/
istream & NOMAD::operator >> ( istream & in , NOMAD::Double & d ) {
  string s;
  in >> s;
  if ( !in.fail() && !d.atof (s) )
    cin.setstate ( ios::failbit );
  return in;
}

/*-----------------------------------------------*/
/*      atof: value determined by a string       */
/*-----------------------------------------------*/
bool NOMAD::Double::atof ( const string & ss ) {
  string s = ss;
  NOMAD::toupper(s);

  if ( s == "-" || s == "INF" ||  s == "-INF" || s == "+INF" ) {
    _value   = 0.0;
    _defined = false;
    return true;
  }

  if ( s.empty() || (s.size() == 1 && !isdigit(s[0])) )
    return false;

  if ( !isdigit(s[0]) && s[0] != '+' && s[0] != '-' && s[0] != '.' )
    return false;

  size_t n = s.size();
  for ( size_t k = 1 ; k < n ; ++k )
    if ( !isdigit(s[k]) && s[k] != '.' ) {
      if ( s[k] == 'E' ) {
 	if ( s.size() == k+1 )
 	  return false;
 	++k;
 	if ( !isdigit(s[k]) && s[k] != '+' && s[k] != '-' )
 	  return false;
      }
      else
 	return false;
    }

  *this = std::atof ( s.c_str() );
  return true;
}

/*------------------------------------------------*/
/* atof from a string that can begin with 'r' to  */
/* indicate a proportion (relative value)         */
/*------------------------------------------------*/
bool NOMAD::Double::relative_atof ( const string & s , bool & relative ) {
  if ( std::toupper(s[0]) == 'R' ) {
    relative  = true;
    string ss = s;
    ss.erase(ss.begin());
    if ( !atof(ss) )
      return false;
    return ( *this >= 0.0 && *this <= 1.0 );
  }
  relative = false;
  return atof(s);
}

/*-----------------------------------------------*/
/*            is the value an integer?           */
/*-----------------------------------------------*/
bool NOMAD::Double::is_integer (void) const {
  if (!_defined)
    return false;
  return ( Double(floor(_value))) == (Double(ceil(_value)) );
}

/*-----------------------------------------------*/
/*             is the value binary ?             */
/*-----------------------------------------------*/
bool NOMAD::Double::is_binary (void) const {
  if (!_defined)
    return false;
  return ( Double(_value) == 0.0 || Double(_value) == 1.0 );
}

/*-------------------------------------*/
/*               d = d1/d2             */
/*-------------------------------------*/
const NOMAD::Double NOMAD::operator / ( const NOMAD::Double & d1 ,
					const NOMAD::Double & d2   ) {
  if ( d2 == 0.0 )
    throw NOMAD::Double::Invalid_Value ( __FILE__ , __LINE__ ,
			 "NOMAD::Double: d1 / d2: division by zero" );
  return NOMAD::Double ( d1.value() / d2.value() );
}

/*-------------------------------------*/
/*                d1 += d2             */
/*-------------------------------------*/
const NOMAD::Double & NOMAD::Double::operator += ( const NOMAD::Double & d2 ) {
  if ( !_defined || !d2._defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double: d1 += d2: d1 or d2 not defined" );
  _value += d2._value;
  return *this;
}

/*-------------------------------------*/
/*               d1 -= d2              */
/*-------------------------------------*/
const NOMAD::Double & NOMAD::Double::operator -= ( const NOMAD::Double & d2 ) {
  if ( !_defined || !d2._defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double: d1 -= d2: d1 or d2 not defined" );
  _value -= d2._value;
  return *this;
}

/*-------------------------------------*/
/*                d1 *= d2             */
/*-------------------------------------*/
const NOMAD::Double & NOMAD::Double::operator *= ( const NOMAD::Double & d2 ) {
  if ( !_defined || !d2._defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double: d1 *= d2: d1 or d2 not defined" );
  _value *= d2._value;
  return *this;
}

/*-------------------------------------*/
/*               d1 /= d2              */
/*-------------------------------------*/
const NOMAD::Double & NOMAD::Double::operator /= ( const NOMAD::Double & d2 ) {
  if ( !_defined || !d2._defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double: d1 /= d2: d1 or d2 not defined" );
  if ( d2 == 0.0 )
    throw Invalid_Value ( __FILE__ , __LINE__ ,
			     "NOMAD::Double: d1 /= d2: division by zero" );
  _value /= d2._value;
  return *this;
}

/*-------------------------------------*/
/*                  ++d                */
/*-------------------------------------*/
NOMAD::Double & NOMAD::Double::operator++ ( void ) {
  if (!_defined)
    throw Not_Defined ( __FILE__ , __LINE__ , "NOMAD::Double: ++d: d not defined" );
  _value += 1;
  return *this;
}

/*-------------------------------------*/
/*                  d++                */
/*-------------------------------------*/
NOMAD::Double NOMAD::Double::operator++ ( int n ) {
  if (!_defined)
    throw Not_Defined ( __FILE__ , __LINE__ , "NOMAD::Double: d++: d not defined" );
  NOMAD::Double tmp = *this;
  if( n <= 0 )
    n = 1;
  _value += n;
  return tmp;
}

/*-------------------------------------*/
/*                --d                  */
/*-------------------------------------*/
NOMAD::Double & NOMAD::Double::operator-- ( void ) {
  if (!_defined)
    throw Not_Defined ( __FILE__ , __LINE__ , "NOMAD::Double: --d: d not defined" );
  _value -= 1;
  return *this;
}

/*-------------------------------------*/
/*                  d--                */
/*-------------------------------------*/
NOMAD::Double NOMAD::Double::operator-- ( int n ) {
  if (!_defined)
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double: d--: d not defined" );
  NOMAD::Double tmp = *this;
  if ( n <= 0 )
    n = 1;
  _value -= n;
  return tmp;
}

/*-------------------------------------*/
/*              operators =            */
/*-------------------------------------*/
NOMAD::Double & NOMAD::Double::operator = ( const NOMAD::Double & d ) {
  _value   = d._value;
  _defined = d._defined;
  return *this;
}

NOMAD::Double & NOMAD::Double::operator = ( double r ) {
  _value   = r;
  _defined = true;
  return *this;
}

/*------------------------------------------*/
/*                  display                 */
/*------------------------------------------*/
void NOMAD::Double::display ( ostream & out ) const {
  if (_defined) {
    if ( _value == INF )
      out << "inf";
    else if ( floor(_value) == ceil(_value) )
      out << static_cast<int>(_value);
    else
      out << _value;
  }
  else
    out << "-";
}

/*------------------------------------------*/
/*                    round                 */
/*------------------------------------------*/
int NOMAD::Double::round ( void ) const {
  if ( !_defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double::round(): value not defined" );
  return static_cast<int>(_value < 0.0 ? -floor(fabs(_value) + .5) : floor(_value + .5));
}

/*------------------------------------------*/
/*                    abs                   */
/*------------------------------------------*/
const NOMAD::Double NOMAD::Double::abs ( void ) const {
  if ( !_defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double::abs(): value not defined" );
  return fabs ( _value );
}

/*------------------------------------------*/
/*                  square                  */
/*------------------------------------------*/
const NOMAD::Double NOMAD::Double::pow2 ( void ) const {
  if ( !_defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double::pow2(): value not defined" );
  return pow ( _value , 2 );
}

/*------------------------------------------*/
/*                square root               */
/*------------------------------------------*/
const NOMAD::Double NOMAD::Double::sqrt ( void ) const {
  if ( !_defined )
    throw Not_Defined ( __FILE__ , __LINE__ ,
			"NOMAD::Double::sqrt(): value not defined" );
  if ( *this < 0.0 )
    throw NOMAD::Double::Invalid_Value ( __FILE__ , __LINE__ ,
		        "NOMAD::Double::sqrt(x): x < 0" );

  return std::sqrt ( _value );
}
