/******************************************************************************
 * Copyright 1998-2019 Lawrence Livermore National Security, LLC and other
 * HYPRE Project Developers. See the top-level COPYRIGHT file for details.
 *
 * SPDX-License-Identifier: (Apache-2.0 OR MIT)
 ******************************************************************************/

#include <assert.h>
#include <math.h>
#include <stdlib.h>

#include "multivector.h"

/* abstract multivector */
struct mv_MultiVector
{
  void*	data;      /* the pointer to the actual multivector */
  HYPRE_Int	ownsData;

  mv_InterfaceInterpreter* interpreter; /* a structure that defines
					      multivector operations */
} ;

void *
mv_MultiVectorGetData (mv_MultiVectorPtr x)
{
  hypre_assert (x!=NULL);
  return x->data;
}

mv_MultiVectorPtr
mv_MultiVectorWrap( mv_InterfaceInterpreter* ii, void * data, HYPRE_Int ownsData )
{
  mv_MultiVectorPtr x;
  
  x = hypre_TAlloc(struct mv_MultiVector, 1, HYPRE_MEMORY_HOST);
  hypre_assert( x != NULL );
  
  x->interpreter = ii;
  x->data = data;
  x->ownsData = ownsData;
  
  return x;
}

mv_MultiVectorPtr 
mv_MultiVectorCreateFromSampleVector( void* ii_, HYPRE_Int n, void* sample ) { 

  mv_MultiVectorPtr x;
  mv_InterfaceInterpreter* ii = (mv_InterfaceInterpreter*)ii_;

  x = hypre_TAlloc(struct mv_MultiVector, 1, HYPRE_MEMORY_HOST);
  hypre_assert( x != NULL );
  
  x->interpreter = ii;
  x->data = (ii->CreateMultiVector)( ii, n, sample );
  x->ownsData = 1;

  return x;
}

mv_MultiVectorPtr 
mv_MultiVectorCreateCopy( mv_MultiVectorPtr x, HYPRE_Int copyValues ) {

  mv_MultiVectorPtr y;
  void* data;
  mv_InterfaceInterpreter* ii;

  hypre_assert( x != NULL );
  ii = x->interpreter;

  y = hypre_TAlloc(struct mv_MultiVector, 1, HYPRE_MEMORY_HOST);
  hypre_assert( y != NULL );
  
  data = (ii->CopyCreateMultiVector)( x->data, copyValues );

  y->interpreter = ii;
  y->data = data;
  y->ownsData = 1;

  return y;
}

void 
mv_MultiVectorDestroy( mv_MultiVectorPtr v) {

  if ( v == NULL )
    return;

  if ( v->ownsData )
    (v->interpreter->DestroyMultiVector)( v->data );
  free( v );
}

void
mv_MultiVectorSetMask( mv_MultiVectorPtr v, HYPRE_Int* mask ) {

  hypre_assert( v != NULL );
  (v->interpreter->SetMask)( v->data, mask );
}

HYPRE_Int
mv_MultiVectorWidth( mv_MultiVectorPtr v ) {

  if ( v == NULL )
    return 0;

  return (v->interpreter->Width)( v->data );
}

HYPRE_Int
mv_MultiVectorHeight( mv_MultiVectorPtr v ) {

  if ( v == NULL )
    return 0;
    	  
  return (v->interpreter->Height)(v->data);
}

void
mv_MultiVectorClear( mv_MultiVectorPtr v ) {

  hypre_assert( v != NULL );
  (v->interpreter->ClearMultiVector)( v->data );
}

void
mv_MultiVectorSetRandom( mv_MultiVectorPtr v, HYPRE_Int seed ) {

  hypre_assert( v != NULL );
  (v->interpreter->SetRandomVectors)( v->data, seed );
}

void 
mv_MultiVectorCopy( mv_MultiVectorPtr src, mv_MultiVectorPtr dest ) {

  hypre_assert( src != NULL && dest != NULL );
  (src->interpreter->CopyMultiVector)( src->data, dest->data );
}

void 
mv_MultiVectorAxpy( HYPRE_Complex a, mv_MultiVectorPtr x, mv_MultiVectorPtr y ) { 
	
  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->MultiAxpy)( a, x->data, y->data );
}

void 
mv_MultiVectorByMultiVector( mv_MultiVectorPtr x, mv_MultiVectorPtr y,
				     HYPRE_Int xyGHeight, HYPRE_Int xyHeight, 
				     HYPRE_Int xyWidth, HYPRE_Real* xy ) { 
/* xy = x'*y */	

  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->MultiInnerProd)
    ( x->data, y->data, xyGHeight, xyHeight, xyWidth, xy );
}

void 
mv_MultiVectorByMultiVectorDiag( mv_MultiVectorPtr x, mv_MultiVectorPtr y,
					 HYPRE_Int* mask, HYPRE_Int n, HYPRE_Real* d ) {
/* d = diag(x'*y) */	

  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->MultiInnerProdDiag)( x->data, y->data, mask, n, d );
}

void 
mv_MultiVectorByMatrix( mv_MultiVectorPtr x, 
			   HYPRE_Int rGHeight, HYPRE_Int rHeight, 
			   HYPRE_Int rWidth, HYPRE_Complex* rVal,
			   mv_MultiVectorPtr y ) {

  /* y = x*r */

  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->MultiVecMat)
    ( x->data, rGHeight, rHeight, rWidth, rVal, y->data );
}

void 
mv_MultiVectorXapy( mv_MultiVectorPtr x, 
		       HYPRE_Int rGHeight, HYPRE_Int rHeight, 
		       HYPRE_Int rWidth, HYPRE_Complex* rVal,
		       mv_MultiVectorPtr y ) {

  /* y = y + x*a */

  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->MultiXapy)
    ( x->data, rGHeight, rHeight, rWidth, rVal, y->data );
}

void 
mv_MultiVectorByDiagonal( mv_MultiVectorPtr x, 
			     HYPRE_Int* mask, HYPRE_Int n, HYPRE_Complex* d,
			     mv_MultiVectorPtr y ) {

  /* y = x*d */

  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->MultiVecMatDiag)( x->data, mask, n, d, y->data );
}

void 
mv_MultiVectorEval( void (*f)( void*, void*, void* ), void* par,
		       mv_MultiVectorPtr x, mv_MultiVectorPtr y ) {

  /* y = f(x) computed vector-wise */

  hypre_assert( x != NULL && y != NULL );
  (x->interpreter->Eval)( f, par, x->data, y->data );
}
							

