#ifndef CLUSTER_STATE_VECTOR_HH
# define CLUSTER_STATE_VECTOR_HH

# include "utils/ArgList.hh"

/** @brief scalar vector
 *
 * This class implments a vector of doubvle with fixed dimension. Such kind
 * of data structure is widely used for clustering/classification.
 *
 * @author Frederic Py <fpy@mbari.org>
 *
 */
class StateVector {
public:
  typedef double scalar_type;

  StateVector();
  explicit StateVector(size_t dim);
  explicit StateVector(Argument const &descr);
  StateVector(StateVector const &other);

  ~StateVector();

  StateVector &operator= (StateVector const &other);

  StateVector &setDimension(size_t dim);

  size_t dimension() const {
    return m_dimension;
  }

  scalar_type const &get(size_t idx) const;
  scalar_type const &operator[](size_t idx) const {
    return get(idx);
  }

  scalar_type &get(size_t idx);
  scalar_type &operator[](size_t idx) {
    return get(idx);
  }

  bool operator==(StateVector const &other) const;
  bool operator!=(StateVector const &other) const {
    return !operator==(other);
  }

# ifdef _QNX 
  /* Workaround to use std::list on QNX.
   *
   * It appears that Watcomm C++ instantiates fully template classes
   * (despite what the standard is saying) so it fails to compile as
   * std::list::merge uses operator<
   *
   * This function has no code in purpose
   */
  bool operator< (StateVector const &other) const {
    return false;
  }
# endif // _QNX

  scalar_type operator* (StateVector const &other) const;
  
  StateVector &operator+=(StateVector const &other);
  StateVector &operator-=(StateVector const &other);
  StateVector &operator*=(scalar_type x);
  StateVector &operator/=(scalar_type x);

  StateVector &normalize(StateVector const &avg, StateVector const &sd);

  scalar_type norm() const;
  
  Symbol toString() const;

private:
  size_t m_dimension;
  scalar_type *m_data;
  
  static scalar_type *create_tab(size_t dim);
}; // StateVector

/** @brief Metric functor.
 *
 * This class provide a generic interface to implements metrics
 * used to compare 2 StateVector. It is strongly recommanded to respect
 * the following metric definition rules :
 * @li 1) d(x,y)>=0 (non-negativity)
 * @li 2) d(x,y)==0 iff x==y (identity of indiscernibles)
 * @li 3) d(x,y)==d(y,x) (symetry)
 * @li 4) d(x, z)<=d(x, y) + d(y, z)  (subadditivity / triangle inequality).
 *
 * You can avoid some of these rules but you have to be aware that
 * you won't have a metric anymore and that it may have a strong impact
 * on the quality of your clustering/classification. For example removing
 * the 2) will implyu that the classifier may consider two different
 * vectors as identical (as the pseudometric returned 0).
 * More details can be found at 
 * http://en.wikipedia.org/wiki/Metric_%28mathematics%29
 */
class StateMetric {
public:
  StateMetric() {}
  virtual ~StateMetric() {}
  
  virtual StateVector::scalar_type operator()(StateVector const &a,
					      StateVector const &b) const =0;
}; // StateMetric

#endif // CLUSTER_STATE_VECTOR_HH
