/*!\file:  Vector.h
 * \brief wrapper to vector objects. The goal is to control which API (PETSc,Scalpack, Plapack?) 
 * implements our underlying vector format.
 */ 

#ifndef _VECTOR_H_
#define _VECTOR_H_

/*Headers:*/
/*{{{*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif
#include "../../toolkits/toolkits.h"
#include "../../EnumDefinitions/EnumDefinitions.h"

/*}}}*/

enum vectortype { PetscVecType, SeqVecType };

template <class doubletype> 
class Vector{

	public:

		int  type;
		#ifdef _HAVE_PETSC_
		PetscVec* pvector;
		#endif
		SeqVec<doubletype>* svector; 

		/*Vector constructors, destructors */
		/*FUNCTION Vector(){{{*/
		Vector(){

			#ifdef _HAVE_PETSC_
			this->pvector=NULL;
			#endif
			this->svector=NULL;

			type=PetscVecType; //default
			#ifndef _HAVE_PETSC_
			type=SeqVecType;
			#endif

		}
		/*}}}*/
		/*FUNCTION Vector(int M,bool fromlocalsize,int in_type){{{*/
		#ifdef _HAVE_PETSC_
		Vector(int M,bool fromlocalsize=false,int in_type=PetscVecType){
		#else
		Vector(int M,bool fromlocalsize=false,int in_type=SeqVecType){
		#endif

			#ifdef _HAVE_PETSC_
			pvector=NULL;
			#endif
			svector=NULL;
			type=in_type;

			if(type==PetscVecType){
			#ifdef _HAVE_PETSC_
				this->pvector=new PetscVec(M,fromlocalsize);
			 #else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
			 #endif
			}
			else if(type==SeqVecType){
				this->svector=new SeqVec<doubletype>(M,fromlocalsize);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION Vector(int m,int M,int in_type){{{*/
		#ifdef _HAVE_PETSC_
		Vector(int m,int M,int in_type=PetscVecType){
		#else
		Vector(int m,int M,int in_type=SeqVecType){
		#endif

			#ifdef _HAVE_PETSC_
			pvector=NULL;
			#endif
			svector=NULL;
			type=in_type;

			if(type==PetscVecType){
			#ifdef _HAVE_PETSC_
				this->pvector=new PetscVec(m,M);
			 #else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
			 #endif
			}
			else if(type==SeqVecType){
				this->svector=new SeqVec<doubletype>(m,M);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION Vector(doubletype* serial_vec,int M,int in_type){{{*/
		#ifdef _HAVE_PETSC_
		Vector(doubletype* serial_vec,int M,int in_type=PetscVecType){
		#else
		Vector(doubletype* serial_vec,int M,int in_type=SeqVecType){
			//} for vim
		#endif

			#ifdef _HAVE_PETSC_
			pvector=NULL;
			#endif

			svector=NULL;
			type=in_type;

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector=new PetscVec(serial_vec,M);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector=new SeqVec<doubletype>(serial_vec,M);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION ~Vector(){{{*/
		~Vector(){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				delete this->pvector;
				#endif
			}
			else if(type==SeqVecType){
				delete this->svector;
			}
		}
		/*}}}*/
		#ifdef _HAVE_PETSC_
		/*FUNCTION Vector(Vec petsc_vector){{{*/
		Vector(Vec petsc_vector){

			this->type=PetscVecType;
			this->svector=NULL;
			this->pvector=new PetscVec(petsc_vector);

		}
		/*}}}*/
		#endif

		/*Vector specific routines*/
		/*FUNCTION Echo{{{*/
		void Echo(void){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->Echo();
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->Echo();
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION Assemble{{{*/
		void Assemble(void){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->Assemble();
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->Assemble();
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION SetValues{{{*/
		void SetValues(int ssize, int* list, doubletype* values, InsMode mode){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->SetValues(ssize,list,values,mode);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->SetValues(ssize,list,values,mode);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION SetValue{{{*/
		void SetValue(int dof, doubletype value, InsMode mode){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->SetValue(dof,value,mode);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->SetValue(dof,value,mode);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION GetValue{{{*/
		void GetValue(doubletype* pvalue,int dof){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->GetValue(pvalue,dof);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->GetValue(pvalue,dof);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION GetSize{{{*/
		void GetSize(int* pM){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->GetSize(pM);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->GetSize(pM);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION IsEmpty{{{*/
		bool IsEmpty(void){

			int M;

			this->GetSize(&M);

			if(M==0) 
				return true;
			else
				return false;
		}
		/*}}}*/
		/*FUNCTION GetLocalSize{{{*/
		void GetLocalSize(int* pM){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->GetLocalSize(pM);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->GetLocalSize(pM);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION Duplicate{{{*/
		Vector* Duplicate(void){

			Vector* output=NULL;

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				output=new Vector();
				output->pvector=this->pvector->Duplicate();
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				output=new Vector();
				output->svector=this->svector->Duplicate();
			}
			else _error_("Vector type: " << type << " not supported yet!");

			return output;

		}
		/*}}}*/
		/*FUNCTION Set{{{*/
		void Set(doubletype value){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->Set(value);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->Set(value);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION AXPY{{{*/
		void AXPY(Vector* X, doubletype a){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->AXPY(X->pvector,a);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->AXPY(X->svector,a);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION AYPX{{{*/
		void AYPX(Vector* X, doubletype a){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->AYPX(X->pvector,a);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->AYPX(X->svector,a);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION ToMPISerial{{{*/
		doubletype* ToMPISerial(void){

			doubletype* vec_serial=NULL;

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				vec_serial=this->pvector->ToMPISerial();
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				vec_serial=this->svector->ToMPISerial();
			}
			else _error_("Vector type: " << type << " not supported yet!");

			return vec_serial;

		}
		/*}}}*/
		/*FUNCTION Copy{{{*/
		void Copy(Vector* to){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->Copy(to->pvector);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->Copy(to->svector);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION Norm{{{*/
		doubletype Norm(NormMode norm_type){

			doubletype norm=0;

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				norm=this->pvector->Norm(norm_type);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				norm=this->svector->Norm(norm_type);
			}
			else _error_("Vector type: " << type << " not supported yet!");

			return norm;
		}
		/*}}}*/
		/*FUNCTION Scale{{{*/
		void Scale(doubletype scale_factor){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->Scale(scale_factor);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->Scale(scale_factor);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
		/*FUNCTION Dot{{{*/
		doubletype Dot(Vector* vector){

			doubletype dot;

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				dot=this->pvector->Dot(vector->pvector);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				dot=this->svector->Dot(vector->svector);
			}
			else _error_("Vector type: " << type << " not supported yet!");

			return dot;
		}
		/*}}}*/
		/*FUNCTION PointwiseDivide{{{*/
		void PointwiseDivide(Vector* x,Vector* y){

			if(type==PetscVecType){
				#ifdef _HAVE_PETSC_
				this->pvector->PointwiseDivide(x->pvector,y->pvector);
				#else
				_error_("Petsc matrix format not usable, as Petsc has not been compiled!");
				#endif
			}
			else if(type==SeqVecType){
				this->svector->PointwiseDivide(x->svector,y->svector);
			}
			else _error_("Vector type: " << type << " not supported yet!");

		}
		/*}}}*/
};
#endif //#ifndef _VECTOR_H_
