/*
 *    vmg - a versatile multigrid solver
 *    Copyright (C) 2012 Institute for Numerical Simulation, University of Bonn
 *
 *  vmg 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.
 *
 *  vmg 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 .
 */
/**
 * @file   vector.hpp
 * @author Julian Iseringhausen 
 * @date   Mon Apr 18 12:26:13 2011
 *
 * @brief  VMG::Vector
 *
 */
#ifndef VECTOR_HPP_
#define VECTOR_HPP_
#include 
#include 
#include 
#include 
namespace VMG
{
class Index;
class Vector
{
public:
  Vector(const Index& index);
  Vector(const vmg_float& x, const vmg_float& y, const vmg_float& z);
  Vector(const vmg_float& val);
  Vector(const vmg_float* arr);
  Vector();
  Vector(const Vector& other)
  {
    std::memcpy(this->i, other.i, 3*sizeof(vmg_float));
  }
  vmg_float& operator[](const int& index) {return i[index];}
  const vmg_float& operator[](const int& index) const {return i[index];}
  vmg_float& X() {return i[0];}
  vmg_float& Y() {return i[1];}
  vmg_float& Z() {return i[2];}
  const vmg_float& X() const {return i[0];}
  const vmg_float& Y() const {return i[1];}
  const vmg_float& Z() const {return i[2];}
  Vector Abs() const {return Vector(fabs(i[0]), fabs(i[1]), fabs(i[2]));}
  vmg_float Length() const {return sqrt(i[0]*i[0]+i[1]*i[1]+i[2]*i[2]);}
  vmg_float Max() const {return std::max(std::max(i[0],i[1]),i[2]);}
  vmg_float Min() const {return std::min(std::min(i[0],i[1]),i[2]);}
  vmg_float Product() const {return i[0]*i[1]*i[2];}
  Vector& Floor()
  {
    i[0] = std::floor(i[0]);
    i[1] = std::floor(i[1]);
    i[2] = std::floor(i[2]);
    return *this;
  }
  bool IsComponentwiseLess(const Vector& other) const
  {
    return this->i[0] < other.i[0]
        && this->i[1] < other.i[1]
        && this->i[2] < other.i[2];
  }
  bool IsComponentwiseLessOrEqual(const Vector& other) const
  {
    return this->i[0] <= other.i[0]
        && this->i[1] <= other.i[1]
        && this->i[2] <= other.i[2];
  }
  bool IsComponentwiseGreater(const Vector& other) const
  {
    return this->i[0] > other.i[0]
        && this->i[1] > other.i[1]
        && this->i[2] > other.i[2];
  }
  bool IsComponentwiseGreaterOrEqual(const Vector& other) const
  {
    return this->i[0] >= other.i[0]
        && this->i[1] >= other.i[1]
        && this->i[2] >= other.i[2];
  }
  bool IsInBounds(const Vector& begin, const Vector& end) const
  {
    return this->IsComponentwiseGreaterOrEqual(begin) &&
           this->IsComponentwiseLessOrEqual(end);
  }
  Vector MaxComponentwise(const Vector& rhs) const
  {
    return Vector(std::max(i[0], rhs.i[0]), std::max(i[1], rhs.i[1]), std::max(i[2], rhs.i[2]));
  }
  Vector MinComponentwise(const Vector& rhs) const
  {
    return Vector(std::min(i[0], rhs.i[0]), std::min(i[1], rhs.i[1]), std::min(i[2], rhs.i[2]));
  }
  vmg_float* vec() {return i;}
  const vmg_float* vec() const {return i;}
  Vector& operator=(const Vector& rhs)
  {
    i[0] = rhs.X();
    i[1] = rhs.Y();
    i[2] = rhs.Z();
    return *this;
  }
  Vector& operator=(const vmg_float& rhs)
  {
    i[0] = rhs;
    i[1] = rhs;
    i[2] = rhs;
    return *this;
  }
  bool operator==(const Vector& other) const
  {
    return (i[0]==other.X() && i[1]==other.Y() && i[2]==other.Z());
  }
  bool operator!=(const Vector& other) const
  {
    return !(*this == other);
  }
  Vector& operator+=(const Vector& rhs)
  {
    i[0] += rhs.X();
    i[1] += rhs.Y();
    i[2] += rhs.Z();
    return *this;
  }
  Vector& operator-=(const Vector& rhs)
  {
    i[0] -= rhs.X();
    i[1] -= rhs.Y();
    i[2] -= rhs.Z();
    return *this;
  }
  Vector& operator*=(const Vector& rhs)
  {
    i[0] *= rhs.X();
    i[1] *= rhs.Y();
    i[2] *= rhs.Z();
    return *this;
  }
  Vector& operator/=(const Vector& rhs)
  {
    i[0] /= rhs.X();
    i[1] /= rhs.Y();
    i[2] /= rhs.Z();
    return *this;
  }
  Vector& operator+=(const vmg_float& rhs)
  {
    i[0] += rhs;
    i[1] += rhs;
    i[2] += rhs;
    return *this;
  }
  Vector& operator-=(const vmg_float& rhs)
  {
    i[0] -= rhs;
    i[1] -= rhs;
    i[2] -= rhs;
    return *this;
  }
  Vector& operator*=(const vmg_float& rhs)
  {
    i[0] *= rhs;
    i[1] *= rhs;
    i[2] *= rhs;
    return *this;
  }
  Vector& operator/=(const vmg_float& rhs)
  {
    i[0] /= rhs;
    i[1] /= rhs;
    i[2] /= rhs;
    return *this;
  }
  Vector operator+(const Vector& rhs) const
  {
    return Vector(*this) += rhs;
  }
  Vector operator-(const Vector& rhs) const
  {
    return Vector(*this) -= rhs;
  }
  Vector operator*(const Vector& rhs) const
  {
    return Vector(*this) *= rhs;
  }
  Vector operator/(const Vector& rhs) const
  {
    return Vector(*this) /= rhs;
  }
  Vector operator+(const vmg_float& rhs) const
  {
    return Vector(*this) += rhs;
  }
  Vector operator-(const vmg_float& rhs) const
  {
    return Vector(*this) -= rhs;
  }
  Vector operator*(const vmg_float& rhs) const
  {
    return Vector(*this) *= rhs;
  }
  Vector operator/(const vmg_float& rhs) const
  {
    return Vector(*this) /= rhs;
  }
private:
  vmg_float i[3];
};
inline Vector operator+(const vmg_float& lhs, const Vector& rhs)
{
  return Vector(lhs) += rhs;
}
inline Vector operator-(const vmg_float& lhs, const Vector& rhs)
{
  return Vector(lhs) -= rhs;
}
inline Vector operator*(const vmg_float& lhs, const Vector& rhs)
{
  return Vector(lhs) *= rhs;
}
inline Vector operator/(const vmg_float& lhs, const Vector& rhs)
{
  return Vector(lhs) /= rhs;
}
inline vmg_float Norm_2(const Vector& vec)
{
  return sqrt(vec.X()*vec.X()+vec.Y()*vec.Y()+vec.Z()*vec.Z());
}
inline vmg_float InnerProduct(const Vector& vec1, const Vector& vec2)
{
  return vec1.X()*vec2.X()+vec1.Y()*vec2.Y()+vec1.Z()*vec2.Z();
}
std::ostream& operator<<(std::ostream& out, const Vector& vector);
}
#endif /* VECTOR_HPP_ */