/*-------------------------------------------------------------------------------------*/
/*  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               */
/*-------------------------------------------------------------------------------------*/
#ifndef __POINT__
#define __POINT__

#include <numeric>
#include <functional>
#include "Double.hpp"

// accumulate operator for squared_norm():
struct squared_norm_op {
  NOMAD::Double operator () ( const NOMAD::Double & d1 , const NOMAD::Double & d2 ) {
    return d1 + d2.pow2();
  }
};

// accumulate operator for size_of():
struct size_of_op {
  int operator () ( int i , const NOMAD::Double & d ) {
    return i + d.size_of();
  }
};

// the class:
// ----------
class Point {

private:

#ifdef DEBUG
  static int      _cardinality;      // number of Point's in memory.
  static int      _max_cardinality;  // max number of Point's in memory.
#endif

  static int      _display_limit;    // max. number of coordinates to display
                                     // default to 20, debug to -1 (no limit)

  int             _n;                // dimension

  NOMAD::Double * _coords;           // coordinates

public:

  /*-------------------------------------------------------------------*/

  // exception class for a bad access :
  class Bad_Access : public NOMAD::Exception {
  public:
    Bad_Access ( const std::string & file ,
		 int                 line ,
		 const std::string & msg    ) :
      NOMAD::Exception ( file , line , msg ) {}
  };

  // exception class for points that are not defined:
  class Not_Defined : public NOMAD::Exception {
  public:
    Not_Defined ( const std::string & file ,
		  int                 line ,
		  const std::string & msg    ) :
      NOMAD::Exception ( file , line , msg ) {}
  };

  // exception class for input errors:
  class Bad_Input : public NOMAD::Exception {
  public:
    Bad_Input ( const std::string & file ,
		int                 line ,
		const std::string & msg    ) :
      NOMAD::Exception ( file , line , msg ) {}
  };

  // exception class for bad operations:
  class Bad_Operation : public NOMAD::Exception {
  public:
    Bad_Operation ( const std::string & file ,
		    int                 line ,
		    const std::string & msg    ) :
      NOMAD::Exception ( file , line , msg ) {}
  };

  /*-------------------------------------------------------------------*/

#ifdef DEBUG
  static int get_cardinality     ( void ) { return Point::_cardinality;     }
  static int get_max_cardinality ( void ) { return Point::_max_cardinality; }
#endif

  // constructor:
  Point ( int n = 0 , const NOMAD::Double & d = NOMAD::Double() );

  // copy constructor:
  Point ( const Point & p );

  // affectation operator:
  const Point & operator = ( const Point & p );

  // destructor:
  virtual ~Point (void);

  // change the Point's dimension and set all coordinates to d:
  void reset ( int n = 0 , const NOMAD::Double & d = NOMAD::Double() );

  // clear the point:
  virtual void clear ( void ) { reset(); }

  // change the Point's dimension (and keep values):
  void resize ( int n );

  // display:
  virtual void display ( std::ostream      & out ,
			 const std::string & sep ,
			 int                 w   ,
			 int                 lim   ) const;

  // change/access the display limit:
  static void set_display_limit ( int dl ) { Point::_display_limit = dl;   }
  static int  get_display_limit ( void   ) { return Point::_display_limit; }

  // const and non-const operators []:
  const NOMAD::Double & operator [] ( int i ) const;
  NOMAD::Double       & operator [] ( int i );

  // sizeof:
  virtual int size_of ( void ) const {
    return std::accumulate ( _coords , _coords+_n , static_cast<int>(sizeof(_n)) , size_of_op() );
  }

  // Point's dimension:
  int size ( void ) const { return _n; }

  // set all the Point's coordinates to a value 'd':
  void set ( const NOMAD::Double & d ) const { std::fill ( _coords , _coords+_n , d ); }

  // set the Point's coordinates with an array:
  void set ( int n , const NOMAD::Double * a );

  // check if all coordinates are defined:
  bool is_complete ( void ) const;

  // check if at least one coordinate is defined:
  virtual bool is_defined ( void ) const;
  
  // sqared norm of the point:
  const NOMAD::Double squared_norm ( void ) const {
    return std::accumulate ( _coords , _coords+_n , NOMAD::Double(0.0) , squared_norm_op() );
  }

  // norm of the point:
  const NOMAD::Double norm ( void ) const { return squared_norm().sqrt(); }

  // dot product:
  const NOMAD::Double dot_product ( const Point & x ) const {
    return std::inner_product ( _coords , _coords+_n , x._coords , NOMAD::Double(0.0) );
  }

  // computation of the angle with another point:
  const NOMAD::Double get_angle ( const Point & y ) const;

  // scalar mutiplication:
  const Point & operator *= ( const NOMAD::Double & d );

  // multiplication:
  const Point operator * ( const Point & p ) const;

  // division:
  const Point operator / ( const Point & p ) const;

  // addition:
  const Point operator + ( const Point & p ) const;

  // subtraction:
  const Point operator - ( const Point & p ) const;

  // negation:
  const Point operator - ( void ) const;

  // comparison operators:
  virtual bool operator  < ( const Point & p ) const;
  bool         operator == ( const Point & p ) const;
  bool         operator != ( const Point & p ) const { return !(*this == p); }

  // input:
  friend std::istream & operator >> ( std::istream & in , Point & p );

};

// display:
inline std::ostream & operator << ( std::ostream & out , const Point & p ) {
  p.display ( out , " " , 2 ,  Point::get_display_limit() );
  return out;
}

#endif
