/*
   BV implemented as an array of independent Vecs

   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   SLEPc - Scalable Library for Eigenvalue Problem Computations
   Copyright (c) 2002-2016, Universitat Politecnica de Valencia, Spain

   This file is part of SLEPc.

   SLEPc is free software: you can redistribute it and/or modify it under  the
   terms of version 3 of the GNU Lesser General Public License as published by
   the Free Software Foundation.

   SLEPc  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 Lesser General Public  License  for
   more details.

   You  should have received a copy of the GNU Lesser General  Public  License
   along with SLEPc. If not, see <http://www.gnu.org/licenses/>.
   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/

#include <slepc/private/bvimpl.h>

typedef struct {
  Vec      *V;
  PetscInt vmip;   /* Version of BVMultInPlace:
       0: memory-efficient version, uses VecGetArray (default in CPU)
       1: version that allocates (e-s) work vectors in every call (default in GPU) */
} BV_VECS;

#undef __FUNCT__
#define __FUNCT__ "BVMult_Vecs"
PetscErrorCode BVMult_Vecs(BV Y,PetscScalar alpha,PetscScalar beta,BV X,Mat Q)
{
  PetscErrorCode ierr;
  BV_VECS        *y = (BV_VECS*)Y->data,*x = (BV_VECS*)X->data;
  PetscScalar    *q,*s=NULL;
  PetscInt       i,j,ldq;

  PetscFunctionBegin;
  if (Q) {
    ierr = MatGetSize(Q,&ldq,NULL);CHKERRQ(ierr);
    if (alpha!=1.0) {
      ierr = BVAllocateWork_Private(Y,X->k-X->l);CHKERRQ(ierr);
      s = Y->work;
    }
    ierr = MatDenseGetArray(Q,&q);CHKERRQ(ierr);
    for (j=Y->l;j<Y->k;j++) {
      ierr = VecScale(y->V[Y->nc+j],beta);CHKERRQ(ierr);
      if (alpha!=1.0) {
        for (i=X->l;i<X->k;i++) s[i-X->l] = alpha*q[i+j*ldq];
      } else s = q+j*ldq+X->l;
      ierr = VecMAXPY(y->V[Y->nc+j],X->k-X->l,s,x->V+X->nc+X->l);CHKERRQ(ierr);
    }
    ierr = MatDenseRestoreArray(Q,&q);CHKERRQ(ierr);
  } else {
    for (j=0;j<Y->k-Y->l;j++) {
      ierr = VecScale(y->V[Y->nc+Y->l+j],beta);CHKERRQ(ierr);
      ierr = VecAXPY(y->V[Y->nc+Y->l+j],alpha,x->V[X->nc+X->l+j]);CHKERRQ(ierr);
    }
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVMultVec_Vecs"
PetscErrorCode BVMultVec_Vecs(BV X,PetscScalar alpha,PetscScalar beta,Vec y,PetscScalar *q)
{
  PetscErrorCode ierr;
  BV_VECS        *x = (BV_VECS*)X->data;
  PetscScalar    *s=NULL;
  PetscInt       i;

  PetscFunctionBegin;
  if (alpha!=1.0) {
    ierr = BVAllocateWork_Private(X,X->k-X->l);CHKERRQ(ierr);
    s = X->work;
  }
  ierr = VecScale(y,beta);CHKERRQ(ierr);
  if (alpha!=1.0) {
    for (i=0;i<X->k-X->l;i++) s[i] = alpha*q[i];
  } else s = q;
  ierr = VecMAXPY(y,X->k-X->l,s,x->V+X->nc+X->l);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVMultInPlace_Vecs_ME"
/*
   BVMultInPlace_Vecs_ME - V(:,s:e-1) = V*Q(:,s:e-1) for regular vectors.

   Memory-efficient version, uses VecGetArray (default in CPU)

   Writing V = [ V1 V2 V3 ] and Q(:,s:e-1) = [ Q1 Q2 Q3 ]', where V2
   corresponds to the columns s:e-1, the computation is done as
                  V2 := V2*Q2 + V1*Q1 + V3*Q3
*/
PetscErrorCode BVMultInPlace_Vecs_ME(BV V,Mat Q,PetscInt s,PetscInt e)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)V->data;
  PetscScalar    *q;
  PetscInt       i,ldq;

  PetscFunctionBegin;
  ierr = MatGetSize(Q,&ldq,NULL);CHKERRQ(ierr);
  ierr = MatDenseGetArray(Q,&q);CHKERRQ(ierr);
  /* V2 := V2*Q2 */
  ierr = BVMultInPlace_Vecs_Private(V,V->n,e-s,ldq,ctx->V+V->nc+s,q+s*ldq+s,PETSC_FALSE);CHKERRQ(ierr);
  /* V2 += V1*Q1 + V3*Q3 */
  for (i=s;i<e;i++) {
    if (s>V->l) {
      ierr = VecMAXPY(ctx->V[V->nc+i],s-V->l,q+i*ldq+V->l,ctx->V+V->nc+V->l);CHKERRQ(ierr);
    }
    if (V->k>e) {
      ierr = VecMAXPY(ctx->V[V->nc+i],V->k-e,q+i*ldq+e,ctx->V+V->nc+e);CHKERRQ(ierr);
    }
  }
  ierr = MatDenseRestoreArray(Q,&q);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVMultInPlace_Vecs_Alloc"
/*
   BVMultInPlace_Vecs_Alloc - V(:,s:e-1) = V*Q(:,s:e-1) for regular vectors.

   Version that allocates (e-s) work vectors in every call (default in GPU)
*/
PetscErrorCode BVMultInPlace_Vecs_Alloc(BV V,Mat Q,PetscInt s,PetscInt e)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)V->data;
  PetscScalar    *q;
  PetscInt       i,ldq;
  Vec            *W;

  PetscFunctionBegin;
  ierr = MatGetSize(Q,&ldq,NULL);CHKERRQ(ierr);
  ierr = MatDenseGetArray(Q,&q);CHKERRQ(ierr);
  ierr = VecDuplicateVecs(V->t,e-s,&W);CHKERRQ(ierr);
  for (i=s;i<e;i++) {
    ierr = VecMAXPY(W[i-s],V->k-V->l,q+i*ldq+V->l,ctx->V+V->nc+V->l);CHKERRQ(ierr);
  }
  for (i=s;i<e;i++) {
    ierr = VecCopy(W[i-s],ctx->V[V->nc+i]);CHKERRQ(ierr);
  }
  ierr = VecDestroyVecs(e-s,&W);CHKERRQ(ierr);
  ierr = MatDenseRestoreArray(Q,&q);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVMultInPlaceTranspose_Vecs"
/*
   BVMultInPlaceTranspose_Vecs - V(:,s:e-1) = V*Q'(:,s:e-1) for regular vectors.
*/
PetscErrorCode BVMultInPlaceTranspose_Vecs(BV V,Mat Q,PetscInt s,PetscInt e)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)V->data;
  PetscScalar    *q;
  PetscInt       i,j,ldq,n;

  PetscFunctionBegin;
  ierr = MatGetSize(Q,&ldq,&n);CHKERRQ(ierr);
  ierr = MatDenseGetArray(Q,&q);CHKERRQ(ierr);
  /* V2 := V2*Q2' */
  ierr = BVMultInPlace_Vecs_Private(V,V->n,e-s,ldq,ctx->V+V->nc+s,q+s*ldq+s,PETSC_TRUE);CHKERRQ(ierr);
  /* V2 += V1*Q1' + V3*Q3' */
  for (i=s;i<e;i++) {
    for (j=V->l;j<s;j++) {
      ierr = VecAXPY(ctx->V[V->nc+i],q[i+j*ldq],ctx->V[V->nc+j]);CHKERRQ(ierr);
    }
    for (j=e;j<n;j++) {
      ierr = VecAXPY(ctx->V[V->nc+i],q[i+j*ldq],ctx->V[V->nc+j]);CHKERRQ(ierr);
    }
  }
  ierr = MatDenseRestoreArray(Q,&q);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVDot_Vecs"
PetscErrorCode BVDot_Vecs(BV X,BV Y,Mat M)
{
  PetscErrorCode ierr;
  BV_VECS        *x = (BV_VECS*)X->data,*y = (BV_VECS*)Y->data;
  PetscScalar    *m;
  PetscInt       j,ldm;

  PetscFunctionBegin;
  ierr = MatGetSize(M,&ldm,NULL);CHKERRQ(ierr);
  ierr = MatDenseGetArray(M,&m);CHKERRQ(ierr);
  for (j=X->l;j<X->k;j++) {
    ierr = VecMDot(x->V[X->nc+j],Y->k-Y->l,y->V+Y->nc+Y->l,m+j*ldm+Y->l);CHKERRQ(ierr);
  }
  ierr = MatDenseRestoreArray(M,&m);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVDotVec_Vecs"
PetscErrorCode BVDotVec_Vecs(BV X,Vec y,PetscScalar *m)
{
  PetscErrorCode ierr;
  BV_VECS        *x = (BV_VECS*)X->data;
  Vec            z = y;

  PetscFunctionBegin;
  if (X->matrix) {
    ierr = BV_IPMatMult(X,y);CHKERRQ(ierr);
    z = X->Bx;
  }
  ierr = VecMDot(z,X->k-X->l,x->V+X->nc+X->l,m);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVDotVec_Begin_Vecs"
PetscErrorCode BVDotVec_Begin_Vecs(BV X,Vec y,PetscScalar *m)
{
  PetscErrorCode ierr;
  BV_VECS        *x = (BV_VECS*)X->data;
  Vec            z = y;

  PetscFunctionBegin;
  if (X->matrix) {
    ierr = BV_IPMatMult(X,y);CHKERRQ(ierr);
    z = X->Bx;
  }
  ierr = VecMDotBegin(z,X->k-X->l,x->V+X->nc+X->l,m);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVDotVec_End_Vecs"
PetscErrorCode BVDotVec_End_Vecs(BV X,Vec y,PetscScalar *m)
{
  PetscErrorCode ierr;
  BV_VECS        *x = (BV_VECS*)X->data;

  PetscFunctionBegin;
  ierr = VecMDotEnd(y,X->k-X->l,x->V+X->nc+X->l,m);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVScale_Vecs"
PetscErrorCode BVScale_Vecs(BV bv,PetscInt j,PetscScalar alpha)
{
  PetscErrorCode ierr;
  PetscInt       i;
  BV_VECS        *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  if (j<0) {
    for (i=bv->l;i<bv->k;i++) {
      ierr = VecScale(ctx->V[bv->nc+i],alpha);CHKERRQ(ierr);
    }
  } else {
    ierr = VecScale(ctx->V[bv->nc+j],alpha);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVNorm_Vecs"
PetscErrorCode BVNorm_Vecs(BV bv,PetscInt j,NormType type,PetscReal *val)
{
  PetscErrorCode ierr;
  PetscInt       i;
  PetscReal      nrm;
  BV_VECS        *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  if (j<0) {
    switch (type) {
    case NORM_FROBENIUS:
      *val = 0.0;
      for (i=bv->l;i<bv->k;i++) {
        ierr = VecNorm(ctx->V[bv->nc+i],NORM_2,&nrm);CHKERRQ(ierr);
        *val += nrm*nrm;
      }
      *val = PetscSqrtReal(*val);
      break;
    default:
      SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Requested norm not implemented in BVVECS");
    }
  } else {
    ierr = VecNorm(ctx->V[bv->nc+j],type,val);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVNorm_Begin_Vecs"
PetscErrorCode BVNorm_Begin_Vecs(BV bv,PetscInt j,NormType type,PetscReal *val)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  if (j<0) {
    switch (type) {
    default:
      SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Requested norm not implemented in BVVECS");
    }
  } else {
    ierr = VecNormBegin(ctx->V[bv->nc+j],type,val);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVNorm_End_Vecs"
PetscErrorCode BVNorm_End_Vecs(BV bv,PetscInt j,NormType type,PetscReal *val)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  if (j<0) {
    switch (type) {
    default:
      SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Requested norm not implemented in BVVECS");
    }
  } else {
    ierr = VecNormEnd(ctx->V[bv->nc+j],type,val);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVMatMult_Vecs"
PetscErrorCode BVMatMult_Vecs(BV V,Mat A,BV W)
{
  PetscErrorCode ierr;
  BV_VECS        *v = (BV_VECS*)V->data,*w = (BV_VECS*)W->data;
  PetscInt       j;

  PetscFunctionBegin;
  if (V->vmm) { ierr = PetscInfo(V,"BVMatMult_Vecs: ignoring method\n");CHKERRQ(ierr); }
  for (j=0;j<V->k-V->l;j++) {
    ierr = MatMult(A,v->V[V->nc+V->l+j],w->V[W->nc+W->l+j]);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVCopy_Vecs"
PetscErrorCode BVCopy_Vecs(BV V,BV W)
{
  PetscErrorCode ierr;
  BV_VECS        *v = (BV_VECS*)V->data,*w = (BV_VECS*)W->data;
  PetscInt       j;

  PetscFunctionBegin;
  for (j=0;j<V->k-V->l;j++) {
    ierr = VecCopy(v->V[V->nc+V->l+j],w->V[W->nc+W->l+j]);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVResize_Vecs"
PetscErrorCode BVResize_Vecs(BV bv,PetscInt m,PetscBool copy)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)bv->data;
  Vec            *newV;
  PetscInt       j;
  char           str[50];

  PetscFunctionBegin;
  ierr = VecDuplicateVecs(bv->t,m,&newV);CHKERRQ(ierr);
  ierr = PetscLogObjectParents(bv,m,newV);CHKERRQ(ierr);
  if (((PetscObject)bv)->name) {
    for (j=0;j<m;j++) {
      ierr = PetscSNPrintf(str,50,"%s_%D",((PetscObject)bv)->name,j);CHKERRQ(ierr);
      ierr = PetscObjectSetName((PetscObject)newV[j],str);CHKERRQ(ierr);
    }
  }
  if (copy) {
    for (j=0;j<PetscMin(m,bv->m);j++) {
      ierr = VecCopy(ctx->V[j],newV[j]);CHKERRQ(ierr);
    }
  }
  ierr = VecDestroyVecs(bv->m,&ctx->V);CHKERRQ(ierr);
  ctx->V = newV;
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVGetColumn_Vecs"
PetscErrorCode BVGetColumn_Vecs(BV bv,PetscInt j,Vec *v)
{
  BV_VECS  *ctx = (BV_VECS*)bv->data;
  PetscInt l;

  PetscFunctionBegin;
  l = BVAvailableVec;
  bv->cv[l] = ctx->V[bv->nc+j];
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVGetArray_Vecs"
PetscErrorCode BVGetArray_Vecs(BV bv,PetscScalar **a)
{
  PetscErrorCode    ierr;
  BV_VECS           *ctx = (BV_VECS*)bv->data;
  PetscInt          j;
  const PetscScalar *p;

  PetscFunctionBegin;
  ierr = PetscMalloc1((bv->nc+bv->m)*bv->n,a);CHKERRQ(ierr);
  for (j=0;j<bv->nc+bv->m;j++) {
    ierr = VecGetArrayRead(ctx->V[j],&p);CHKERRQ(ierr);
    ierr = PetscMemcpy(*a+j*bv->n,p,bv->n*sizeof(PetscScalar));CHKERRQ(ierr);
    ierr = VecRestoreArrayRead(ctx->V[j],&p);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVRestoreArray_Vecs"
PetscErrorCode BVRestoreArray_Vecs(BV bv,PetscScalar **a)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)bv->data;
  PetscInt       j;
  PetscScalar    *p;

  PetscFunctionBegin;
  for (j=0;j<bv->nc+bv->m;j++) {
    ierr = VecGetArray(ctx->V[j],&p);CHKERRQ(ierr);
    ierr = PetscMemcpy(p,*a+j*bv->n,bv->n*sizeof(PetscScalar));CHKERRQ(ierr);
    ierr = VecRestoreArray(ctx->V[j],&p);CHKERRQ(ierr);
  }
  ierr = PetscFree(*a);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVGetArrayRead_Vecs"
PetscErrorCode BVGetArrayRead_Vecs(BV bv,const PetscScalar **a)
{
  PetscErrorCode    ierr;
  BV_VECS           *ctx = (BV_VECS*)bv->data;
  PetscInt          j;
  const PetscScalar *p;

  PetscFunctionBegin;
  ierr = PetscMalloc1((bv->nc+bv->m)*bv->n,a);CHKERRQ(ierr);
  for (j=0;j<bv->nc+bv->m;j++) {
    ierr = VecGetArrayRead(ctx->V[j],&p);CHKERRQ(ierr);
    ierr = PetscMemcpy((PetscScalar*)*a+j*bv->n,p,bv->n*sizeof(PetscScalar));CHKERRQ(ierr);
    ierr = VecRestoreArrayRead(ctx->V[j],&p);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVRestoreArrayRead_Vecs"
PetscErrorCode BVRestoreArrayRead_Vecs(BV bv,const PetscScalar **a)
{
  PetscErrorCode ierr;

  PetscFunctionBegin;
  ierr = PetscFree(*a);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVVecsSetVmip"
/*
   Sets the value of vmip flag and resets ops->multinplace accordingly
 */
PETSC_STATIC_INLINE PetscErrorCode BVVecsSetVmip(BV bv,PetscInt vmip)
{
  typedef PetscErrorCode (*fmultinplace)(BV,Mat,PetscInt,PetscInt);
  fmultinplace multinplace[2] = {BVMultInPlace_Vecs_ME, BVMultInPlace_Vecs_Alloc};
  BV_VECS      *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  ctx->vmip            = vmip;
  bv->ops->multinplace = multinplace[vmip];
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVSetFromOptions_Vecs"
PetscErrorCode BVSetFromOptions_Vecs(PetscOptionItems *PetscOptionsObject,BV bv)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  ierr = PetscOptionsHead(PetscOptionsObject,"BV Vecs Options");CHKERRQ(ierr);
    ierr = PetscOptionsInt("-bv_vecs_vmip","Version of BVMultInPlace operation","",ctx->vmip,&ctx->vmip,NULL);CHKERRQ(ierr);
    if (ctx->vmip<0 || ctx->vmip>1) SETERRQ(PetscObjectComm((PetscObject)bv),PETSC_ERR_ARG_OUTOFRANGE,"Wrong version of BVMultInPlace");
    ierr = BVVecsSetVmip(bv,ctx->vmip);CHKERRQ(ierr);
  ierr = PetscOptionsTail();CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVView_Vecs"
PetscErrorCode BVView_Vecs(BV bv,PetscViewer viewer)
{
  PetscErrorCode    ierr;
  BV_VECS           *ctx = (BV_VECS*)bv->data;
  PetscInt          j;
  PetscViewerFormat format;
  PetscBool         isascii,ismatlab=PETSC_FALSE;
  const char        *bvname,*name;

  PetscFunctionBegin;
  ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii);CHKERRQ(ierr);
  if (isascii) {
    ierr = PetscViewerGetFormat(viewer,&format);CHKERRQ(ierr);
    if (format == PETSC_VIEWER_ASCII_MATLAB) ismatlab = PETSC_TRUE;
  }
  if (ismatlab) {
    ierr = PetscObjectGetName((PetscObject)bv,&bvname);CHKERRQ(ierr);
    ierr = PetscViewerASCIIPrintf(viewer,"%s=[];\n",bvname);CHKERRQ(ierr);
  }
  for (j=bv->nc;j<bv->nc+bv->m;j++) {
    ierr = VecView(ctx->V[j],viewer);CHKERRQ(ierr);
    if (ismatlab) {
      ierr = PetscObjectGetName((PetscObject)ctx->V[j],&name);CHKERRQ(ierr);
      ierr = PetscViewerASCIIPrintf(viewer,"%s=[%s,%s];clear %s\n",bvname,bvname,name,name);CHKERRQ(ierr);
    }
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVDestroy_Vecs"
PetscErrorCode BVDestroy_Vecs(BV bv)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)bv->data;

  PetscFunctionBegin;
  ierr = VecDestroyVecs(bv->nc+bv->m,&ctx->V);CHKERRQ(ierr);
  ierr = PetscFree(bv->data);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVDuplicate_Vecs"
PetscErrorCode BVDuplicate_Vecs(BV V,BV *W)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx = (BV_VECS*)V->data;

  PetscFunctionBegin;
  ierr = BVVecsSetVmip(*W,ctx->vmip);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BVCreate_Vecs"
PETSC_EXTERN PetscErrorCode BVCreate_Vecs(BV bv)
{
  PetscErrorCode ierr;
  BV_VECS        *ctx;
  PetscInt       j;
  PetscBool      iscusp;
  char           str[50];

  PetscFunctionBegin;
  ierr = PetscNewLog(bv,&ctx);CHKERRQ(ierr);
  bv->data = (void*)ctx;

  ierr = VecDuplicateVecs(bv->t,bv->m,&ctx->V);CHKERRQ(ierr);
  ierr = PetscLogObjectParents(bv,bv->m,ctx->V);CHKERRQ(ierr);
  if (((PetscObject)bv)->name) {
    for (j=0;j<bv->m;j++) {
      ierr = PetscSNPrintf(str,50,"%s_%D",((PetscObject)bv)->name,j);CHKERRQ(ierr);
      ierr = PetscObjectSetName((PetscObject)ctx->V[j],str);CHKERRQ(ierr);
    }
  }

  /* Default version of BVMultInPlace */
  ierr = PetscObjectTypeCompareAny((PetscObject)bv->t,&iscusp,VECSEQCUSP,VECMPICUSP,"");CHKERRQ(ierr);
  ctx->vmip = iscusp? 1: 0;

  /* Deferred call to setfromoptions */
  if (bv->defersfo) {
    ierr = PetscObjectOptionsBegin((PetscObject)bv);CHKERRQ(ierr);
    ierr = BVSetFromOptions_Vecs(PetscOptionsObject,bv);CHKERRQ(ierr);
    ierr = PetscOptionsEnd();CHKERRQ(ierr);
  }
  ierr = BVVecsSetVmip(bv,ctx->vmip);CHKERRQ(ierr);

  bv->ops->mult             = BVMult_Vecs;
  bv->ops->multvec          = BVMultVec_Vecs;
  bv->ops->multinplacetrans = BVMultInPlaceTranspose_Vecs;
  bv->ops->dot              = BVDot_Vecs;
  bv->ops->dotvec           = BVDotVec_Vecs;
  bv->ops->dotvec_begin     = BVDotVec_Begin_Vecs;
  bv->ops->dotvec_end       = BVDotVec_End_Vecs;
  bv->ops->scale            = BVScale_Vecs;
  bv->ops->norm             = BVNorm_Vecs;
  bv->ops->norm_begin       = BVNorm_Begin_Vecs;
  bv->ops->norm_end         = BVNorm_End_Vecs;
  bv->ops->matmult          = BVMatMult_Vecs;
  bv->ops->copy             = BVCopy_Vecs;
  bv->ops->resize           = BVResize_Vecs;
  bv->ops->getcolumn        = BVGetColumn_Vecs;
  bv->ops->getarray         = BVGetArray_Vecs;
  bv->ops->restorearray     = BVRestoreArray_Vecs;
  bv->ops->getarrayread     = BVGetArrayRead_Vecs;
  bv->ops->restorearrayread = BVRestoreArrayRead_Vecs;
  bv->ops->destroy          = BVDestroy_Vecs;
  bv->ops->duplicate        = BVDuplicate_Vecs;
  bv->ops->setfromoptions   = BVSetFromOptions_Vecs;
  bv->ops->view             = BVView_Vecs;
  PetscFunctionReturn(0);
}

