/***************************************************************************
 *   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++ Implementation: physiksvector
//
// Description: Definition of Physiks::Vector.
//
//
// Author: Harish Kukreja <harish.kukreja@gmail.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>

#include "physiksvector.h"

using namespace Physiks;

class Vector::Private
{
  public:
    Private(const u_int s)
      : dimension(s)
    {
      cont = gsl_vector_alloc(dimension);
    }

    ~Private()
    {
      gsl_vector_free(cont);
    }

    /** The dimension of the Vector. */
    const u_int dimension;

    /** The gsl_vector core. Shortened version of "contents". */
    gsl_vector *cont;
};


Physiks::Vector::Vector(const u_int size, const PHYSCALAR init) : d(new Private(size)) {
  gsl_vector_set_all(d->cont, init);
}

Physiks::Vector::Vector() : d(new Private(3)) {
}

Physiks::Vector::Vector(const Vector &init) : d(new Private(init.d->dimension)) {
  gsl_vector_memcpy(d->cont, init.d->cont);
}

Physiks::Vector::Vector(const u_int size, PHYSCALAR arr[]) : d(new Private(size)) {
  for (u_int i = 0; i < size; ++i) gsl_vector_set(d->cont, i, arr[i]);
}

Physiks::Vector::~Vector() {
  delete d;
}

Vector Physiks::Vector::scale(const Vector &vec, const PHYSCALAR fact) {
  Vector res(vec);
  gsl_vector_scale(res.d->cont, fact);
  return res;
}

void Physiks::Vector::set_zero(Vector &vec) {
  gsl_vector_set_zero(vec.d->cont);
}

void Physiks::Vector::set(const Vector &vec) {
  gsl_vector_memcpy(d->cont, vec.d->cont);
}

void Physiks::Vector::set(const u_int index, const PHYSCALAR value) {
  if (index < d->dimension) gsl_vector_set(d->cont, index, value);
}

PHYSCALAR Physiks::Vector::norm(const Vector &vec) {
  return gsl_blas_dnrm2(vec.d->cont);
}

Vector Physiks::Vector::normalize(const Vector &vec) {
  Vector res(vec);
  int valid = gsl_vector_scale(res.d->cont, 1.0 / gsl_blas_dnrm2(res.d->cont));
  return (valid != 0) ? vec : res;
}

u_int Physiks::Vector::dim() const {
  return d->dimension;
}

PHYSCALAR Physiks::Vector::get(const u_int index) const {
  return (index < d->dimension) ? gsl_vector_get(d->cont, index) : 0.0;
}

Vector Physiks::Vector::cross(const Vector &lhs, const Vector &rhs) {
  Vector res(lhs.d->dimension, 0.0);
  res.set(0, lhs[1] * rhs[2] - lhs[2] * rhs[1]);
  res.set(1, lhs[2] * rhs[0] - lhs[0] * rhs[2]);
  res.set(2, lhs[0] * rhs[1] - lhs[1] * rhs[0]);
  return res;
}

PHYSCALAR Physiks::Vector::dot(const Vector &lhs, const Vector &rhs) {
  PHYSCALAR res;
  gsl_blas_ddot(lhs.d->cont, rhs.d->cont, &res);
  return res;
}

Vector& Physiks::Vector::operator=(const Vector &init) {
  delete d;
  d = new Private(init.d->dimension);
  gsl_vector_memcpy(d->cont, init.d->cont);
  return *this;
}

Vector Physiks::Vector::operator+(const Vector &rhs) const {
  Vector res(*this);
  gsl_vector_add(res.d->cont, rhs.d->cont);
  return res;
}

void Physiks::Vector::operator+=(const Vector &rhs) {
  gsl_vector_add(d->cont, rhs.d->cont);
}

void Physiks::Vector::operator-=(const Vector &rhs) {
  gsl_vector_sub(d->cont, rhs.d->cont);
}

Vector Physiks::Vector::operator-(const Vector &rhs) const {
  Vector res(*this);
  gsl_vector_sub(res.d->cont, rhs.d->cont);
  return res;
}

Vector Physiks::Vector::operator-() const {
  Vector res(*this);
  gsl_vector_scale(res.d->cont, -1.0);
  return res;
}

PHYSCALAR Physiks::Vector::operator*(const Vector &rhs) const {
  return Vector::dot(*this, rhs);
}

Vector Physiks::Vector::operator*(PHYSCALAR fac) const {
  return Vector::scale(*this, fac);
}

Vector Physiks::operator*(const PHYSCALAR fac, const Vector &rhs) {
  return Vector::scale(rhs, fac);
}

PHYSCALAR Physiks::Vector::operator[](const u_int k) const {
  return get(k);
}
