/***************************************************************************
 *   Copyright (C) 2006 by Harish Kukreja                                  *
 *   harish.kukreja@gmail.com                                              *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//
// C++ Interface: physiksvector
//
// Description: Wrapper class around GSL's gsl_vector. Keeps SWIG happy and developers sane.
//
//
// Author: Harish Kukreja <harish.kukreja@gmail.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//

#ifndef _PHYSIKSVECTOR_H_
#define _PHYSIKSVECTOR_H_

#include "physiks_export.h"
#include "globaldefs.h"

namespace Physiks {

/** \class Vector
 * A simple wrapper class around gsl/gsl_vector. Without this, one would have an unpleasant
 * blend of C-style code alongside C++ code. Note that it is work in progress, with new
 * functions added when they are needed. */
class PHYSIKS_EXPORT Vector {
  public:
    /** \brief  Constructor 1.
     * \param size The dimension of the vector
     * \param init the initialisation value of all vector elements */
    Vector(const u_int size, const PHYSCALAR init = 0.0);

    /** \brief  (Hopefully) temporary constructor. It's not really essential except for
     * patching a rather strange compile error (comment this constructor and see if you 
     * can fix the compile error). */
    Vector();

    /** \brief  Copy Constructor.
     * \param init the reference Vector */
    Vector(const Vector &init);

    /** \brief  C-style array constructor.
     * \param size  The length of the array
     * \param arr   The array itself    */
    Vector(const u_int size, PHYSCALAR arr[]);

    virtual ~Vector();


    /** \brief  Immutable scaling of the given vector.
     * \param vec   The vector to be scaled
     * \param fac   The scaling factor
     * \return  The given vector multiplied by the given scalar */
    static Vector scale(const Vector &vec, const PHYSCALAR fac);

    /** \brief  Sets the given vector to the zero vector.
     * \param vec   The vector to be zeroed */
    static void set_zero(Vector &vec);

    void set(const Vector &vec);

    /** \brief  Set the value of a coordinate. Does nothing if given an invalid index.
     * \param index the index of the coordinate (0-based)
     * \param value the value to change the coordinate to */
    void set(const u_int index, const PHYSCALAR value);

    /** \brief  The Euclidean norm (magnitude) of the Vector.
     * \param vec   The vector of which the norm must be computed
     * \return the euclidean norm. */
    static PHYSCALAR norm(const Vector &vec);

    /** \brief  A unit vector in the same direction as the calling vector.
     * \param vec   The vector to normalize
     * \return a unit vector in the same direction as the given vector */
    static Vector normalize(const Vector &vec);

    /** \brief  Returns the dimension of the vector.
     * \return calling vector's dimension */
    u_int dim() const;

    /** \brief  Get accessor for an arbitrary coordinate.
     * (returns 0.0 if given an invalid index).
     * \param index The coordinate's index (0-based).
     * \return the value of the respective coordinate. 0 if index is out of bounds */
    PHYSCALAR get(const u_int index) const;

    /** \brief  Vector cross product. Does not check dimensions of cross-product operands, so
     *          vectors of dimension other than 3 might yield non-sensical results.
     * \param   lhs the 1st operand of the cross product.
     * \param   rhs the 2nd operand of the cross product.
     * \return  the resultant vector, orthogonal to both arguments. */
    static Vector cross(const Vector &lhs, const Vector &rhs);

    /** \brief  Standard Euclidean inner product.
     * \param lhs   The left argument of the inner product
     * \param rhs   The right argument of the inner product
     * \return  The result of the Euclidean inner product of the given vectors  */
    static PHYSCALAR dot(const Vector &lhs, const Vector &rhs);


    /** Copy operator. */
    Vector& operator=(const Vector &init);

    /** Vector addition overload. */
    Vector operator+(const Vector &rhs) const;

    /** Vector addition & assignment overload. */
    void operator+=(const Vector &rhs);

    /** Vector subtraction & assignment overload. */
    void operator-=(const Vector &rhs);

    /** Vector subtraction overload. */
    Vector operator-(const Vector &rhs) const;

    /** Vector negation overload. */
    Vector operator-() const;

    /** Vector standard inner product (dot product) overload. */
    PHYSCALAR operator*(const Vector &rhs) const;

    /** Vector scalar multiplication overload. */
    Vector operator*(const PHYSCALAR fac) const;

    /** Vector scalar multiplication overload. */
    friend Vector operator*(const PHYSCALAR fac, const Vector &rhs);

    /** Vector indexing (coordinate access) function. Returns 0 if given an invalid index */
    PHYSCALAR operator[](const u_int k) const;


  private:
    class Private;
    Private * d;
};

}   // End namespace Physiks.

#endif
