#include <../../nrnconf.h>

/**************************************************************************
**
** Copyright (C) 1993 David E. Steward & Zbigniew Leyk, all rights reserved.
**
**			     Meschach Library
** 
** This Meschach Library is provided "as is" without any express 
** or implied warranty of any kind with respect to this software. 
** In particular the authors shall not be liable for any direct, 
** indirect, special, incidental or consequential damages arising 
** in any way from use of the software.
** 
** Everyone is granted permission to copy, modify and redistribute this
** Meschach Library, provided:
**  1.  All copies contain this copyright notice.
**  2.  All modified copies shall carry a notice stating who
**      made the last modification and the date of such modification.
**  3.  No charge is made for this software or works derived from it.  
**      This clause shall not be construed as constraining other software
**      distributed on the same medium as this software, nor is a
**      distribution fee considered a charge.
**
***************************************************************************/


#include	<stdio.h>
#include	"matrix.h"
#include	"zmatrix.h"
static	char	rcsid[] = "zvecop.c,v 1.1 1997/12/04 17:56:19 hines Exp";



/* _zin_prod -- inner product of two vectors from i0 downwards
	-- flag != 0 means compute sum_i a[i]*.b[i];
	-- flag == 0 means compute sum_i a[i].b[i] */
complex	_zin_prod(a,b,i0,flag)
ZVEC	*a,*b;
u_int	i0, flag;
{
	u_int	limit;

	if ( a==ZVNULL || b==ZVNULL )
		error(E_NULL,"_zin_prod");
	limit = min(a->dim,b->dim);
	if ( i0 > limit )
		error(E_BOUNDS,"_zin_prod");

	return __zip__(&(a->ve[i0]),&(b->ve[i0]),(int)(limit-i0),flag);
}

/* zv_mlt -- scalar-vector multiply -- may be in-situ */
ZVEC	*zv_mlt(scalar,vector,out)
complex	scalar;
ZVEC	*vector,*out;
{
	/* u_int	dim, i; */
	/* complex	*out_ve, *vec_ve; */

	if ( vector==ZVNULL )
		error(E_NULL,"zv_mlt");
	if ( out==ZVNULL || out->dim != vector->dim )
		out = zv_resize(out,vector->dim);
	if ( scalar.re == 0.0 && scalar.im == 0.0 )
		return zv_zero(out);
	if ( scalar.re == 1.0 && scalar.im == 0.0 )
		return zv_copy(vector,out);

	__zmlt__(vector->ve,scalar,out->ve,(int)(vector->dim));

	return (out);
}

/* zv_add -- vector addition -- may be in-situ */
ZVEC	*zv_add(vec1,vec2,out)
ZVEC	*vec1,*vec2,*out;
{
	u_int	dim;

	if ( vec1==ZVNULL || vec2==ZVNULL )
		error(E_NULL,"zv_add");
	if ( vec1->dim != vec2->dim )
		error(E_SIZES,"zv_add");
	if ( out==ZVNULL || out->dim != vec1->dim )
		out = zv_resize(out,vec1->dim);
	dim = vec1->dim;
	__zadd__(vec1->ve,vec2->ve,out->ve,(int)dim);

	return (out);
}

/* zv_mltadd -- scalar/vector multiplication and addition
		-- out = v1 + scale.v2		*/
ZVEC	*zv_mltadd(v1,v2,scale,out)
ZVEC	*v1,*v2,*out;
complex	scale;
{
	/* register u_int	dim, i; */
	/* complex	*out_ve, *v1_ve, *v2_ve; */

	if ( v1==ZVNULL || v2==ZVNULL )
		error(E_NULL,"zv_mltadd");
	if ( v1->dim != v2->dim )
		error(E_SIZES,"zv_mltadd");
	if ( scale.re == 0.0 && scale.im == 0.0 )
		return zv_copy(v1,out);
	if ( scale.re == 1.0 && scale.im == 0.0 )
		return zv_add(v1,v2,out);

	if ( v2 != out )
	{
	    tracecatch(out = zv_copy(v1,out),"zv_mltadd");

	    /* dim = v1->dim; */
	    __zmltadd__(out->ve,v2->ve,scale,(int)(v1->dim),0);
	}
	else
	{
	    tracecatch(out = zv_mlt(scale,v2,out),"zv_mltadd");
	    out = zv_add(v1,out,out);
	}

	return (out);
}

/* zv_sub -- vector subtraction -- may be in-situ */
ZVEC	*zv_sub(vec1,vec2,out)
ZVEC	*vec1,*vec2,*out;
{
	/* u_int	i, dim; */
	/* complex	*out_ve, *vec1_ve, *vec2_ve; */

	if ( vec1==ZVNULL || vec2==ZVNULL )
		error(E_NULL,"zv_sub");
	if ( vec1->dim != vec2->dim )
		error(E_SIZES,"zv_sub");
	if ( out==ZVNULL || out->dim != vec1->dim )
		out = zv_resize(out,vec1->dim);

	__zsub__(vec1->ve,vec2->ve,out->ve,(int)(vec1->dim));

	return (out);
}

/* zv_map -- maps function f over components of x: out[i] = f(x[i])
	-- _zv_map sets out[i] = f(x[i],params) */
ZVEC	*zv_map(f,x,out)
#ifdef PROTOYPES_IN_STRUCT
complex	(*f)(complex);
#else
complex (*f)();
#endif
ZVEC	*x, *out;
{
	complex	*x_ve, *out_ve;
	int	i, dim;

	if ( ! x || ! f )
		error(E_NULL,"zv_map");
	if ( ! out || out->dim != x->dim )
		out = zv_resize(out,x->dim);

	dim = x->dim;	x_ve = x->ve;	out_ve = out->ve;
	for ( i = 0; i < dim; i++ )
		out_ve[i] = (*f)(x_ve[i]);

	return out;
}

ZVEC	*_zv_map(f,params,x,out)
#ifdef PROTOTYPES_IN_STRUCT
complex	(*f)(void *,complex);
#else
complex	(*f)();
#endif
ZVEC	*x, *out;
void	*params;
{
	complex	*x_ve, *out_ve;
	int	i, dim;

	if ( ! x || ! f )
		error(E_NULL,"_zv_map");
	if ( ! out || out->dim != x->dim )
		out = zv_resize(out,x->dim);

	dim = x->dim;	x_ve = x->ve;	out_ve = out->ve;
	for ( i = 0; i < dim; i++ )
		out_ve[i] = (*f)(params,x_ve[i]);

	return out;
}

/* zv_lincomb -- returns sum_i a[i].v[i], a[i] real, v[i] vectors */
ZVEC	*zv_lincomb(n,v,a,out)
int	n;	/* number of a's and v's */
complex	a[];
ZVEC	*v[], *out;
{
	int	i;

	if ( ! a || ! v )
		error(E_NULL,"zv_lincomb");
	if ( n <= 0 )
		return ZVNULL;

	for ( i = 1; i < n; i++ )
		if ( out == v[i] )
		    error(E_INSITU,"zv_lincomb");

	out = zv_mlt(a[0],v[0],out);
	for ( i = 1; i < n; i++ )
	{
		if ( ! v[i] )
			error(E_NULL,"zv_lincomb");
		if ( v[i]->dim != out->dim )
			error(E_SIZES,"zv_lincomb");
		out = zv_mltadd(out,v[i],a[i],out);
	}

	return out;
}


#ifdef ANSI_C


/* zv_linlist -- linear combinations taken from a list of arguments;
   calling:
      zv_linlist(out,v1,a1,v2,a2,...,vn,an,NULL);
   where vi are vectors (ZVEC *) and ai are numbers (complex)
*/

ZVEC	*zv_linlist(ZVEC *out,ZVEC *v1,complex a1,...)
{
   va_list ap;
   ZVEC *par;
   complex a_par;

   if ( ! v1 )
     return ZVNULL;
   
   va_start(ap, a1);
   out = zv_mlt(a1,v1,out);
   
   while ((par = va_arg(ap,ZVEC *))) {   /* NULL ends the list*/
      a_par = va_arg(ap,complex);
      if (a_par.re == 0.0 && a_par.im == 0.0) continue;
      if ( out == par )		
	error(E_INSITU,"zv_linlist");
      if ( out->dim != par->dim )	
	error(E_SIZES,"zv_linlist");

      if (a_par.re == 1.0 && a_par.im == 0.0)
	out = zv_add(out,par,out);
      else if (a_par.re == -1.0 && a_par.im == 0.0)
	out = zv_sub(out,par,out);
      else
	out = zv_mltadd(out,par,a_par,out); 
   } 
   
   va_end(ap);
   return out;
}


#elif VARARGS

/* zv_linlist -- linear combinations taken from a list of arguments;
   calling:
      zv_linlist(out,v1,a1,v2,a2,...,vn,an,NULL);
   where vi are vectors (ZVEC *) and ai are numbers (complex)
*/
ZVEC  *zv_linlist(va_alist) va_dcl
{
   va_list ap;
   ZVEC *par, *out;
   complex a_par;

   va_start(ap);
   out = va_arg(ap,ZVEC *);
   par = va_arg(ap,ZVEC *);
   if ( ! par ) {
      va_end(ap);
      return ZVNULL;
   }
   
   a_par = va_arg(ap,complex);
   out = zv_mlt(a_par,par,out);
   
   while ((par = va_arg(ap,ZVEC *))) {   /* NULL ends the list*/
      a_par = va_arg(ap,complex);
      if (a_par.re == 0.0 && a_par.im == 0.0) continue;
      if ( out == par )		
	error(E_INSITU,"zv_linlist");
      if ( out->dim != par->dim )	
	error(E_SIZES,"zv_linlist");

      if (a_par.re == 1.0 && a_par.im == 0.0)
	out = zv_add(out,par,out);
      else if (a_par.re == -1.0 && a_par.im == 0.0)
	out = zv_sub(out,par,out);
      else
	out = zv_mltadd(out,par,a_par,out); 
   } 
   
   va_end(ap);
   return out;
}


#endif



/* zv_star -- computes componentwise (Hadamard) product of x1 and x2
	-- result out is returned */
ZVEC	*zv_star(x1, x2, out)
ZVEC	*x1, *x2, *out;
{
    int		i;
    Real	t_re, t_im;

    if ( ! x1 || ! x2 )
	error(E_NULL,"zv_star");
    if ( x1->dim != x2->dim )
	error(E_SIZES,"zv_star");
    out = zv_resize(out,x1->dim);

    for ( i = 0; i < x1->dim; i++ )
    {
	/* out->ve[i] = x1->ve[i] * x2->ve[i]; */
	t_re = x1->ve[i].re*x2->ve[i].re - x1->ve[i].im*x2->ve[i].im;
	t_im = x1->ve[i].re*x2->ve[i].im + x1->ve[i].im*x2->ve[i].re;
	out->ve[i].re = t_re;
	out->ve[i].im = t_im;
    }

    return out;
}

/* zv_slash -- computes componentwise ratio of x2 and x1
	-- out[i] = x2[i] / x1[i]
	-- if x1[i] == 0 for some i, then raise E_SING error
	-- result out is returned */
ZVEC	*zv_slash(x1, x2, out)
ZVEC	*x1, *x2, *out;
{
    int		i;
    Real	r2, t_re, t_im;
    complex	tmp;

    if ( ! x1 || ! x2 )
	error(E_NULL,"zv_slash");
    if ( x1->dim != x2->dim )
	error(E_SIZES,"zv_slash");
    out = zv_resize(out,x1->dim);

    for ( i = 0; i < x1->dim; i++ )
    {
	r2 = x1->ve[i].re*x1->ve[i].re + x1->ve[i].im*x1->ve[i].im;
	if ( r2 == 0.0 )
	    error(E_SING,"zv_slash");
	tmp.re =   x1->ve[i].re / r2;
	tmp.im = - x1->ve[i].im / r2;
	t_re = tmp.re*x2->ve[i].re - tmp.im*x2->ve[i].im;
	t_im = tmp.re*x2->ve[i].im - tmp.im*x2->ve[i].re;
	out->ve[i].re = t_re;
	out->ve[i].im = t_im;
    }

    return out;
}

/* zv_sum -- returns sum of entries of a vector */
complex	zv_sum(x)
ZVEC	*x;
{
    int		i;
    complex	sum;

    if ( ! x )
	error(E_NULL,"zv_sum");

    sum.re = sum.im = 0.0;
    for ( i = 0; i < x->dim; i++ )
    {
	sum.re += x->ve[i].re;
	sum.im += x->ve[i].im;
    }

    return sum;
}

/* px_zvec -- permute vector */
ZVEC	*px_zvec(px,vector,out)
PERM	*px;
ZVEC	*vector,*out;
{
    u_int	old_i, i, size, start;
    complex	tmp;
    
    if ( px==PNULL || vector==ZVNULL )
	error(E_NULL,"px_zvec");
    if ( px->size > vector->dim )
	error(E_SIZES,"px_zvec");
    if ( out==ZVNULL || out->dim < vector->dim )
	out = zv_resize(out,vector->dim);
    
    size = px->size;
    if ( size == 0 )
	return zv_copy(vector,out);
    
    if ( out != vector )
    {
	for ( i=0; i<size; i++ )
	    if ( px->pe[i] >= size )
		error(E_BOUNDS,"px_vec");
	    else
		out->ve[i] = vector->ve[px->pe[i]];
    }
    else
    {	/* in situ algorithm */
	start = 0;
	while ( start < size )
	{
	    old_i = start;
	    i = px->pe[old_i];
	    if ( i >= size )
	    {
		start++;
		continue;
	    }
	    tmp = vector->ve[start];
	    while ( TRUE )
	    {
		vector->ve[old_i] = vector->ve[i];
		px->pe[old_i] = i+size;
		old_i = i;
		i = px->pe[old_i];
		if ( i >= size )
		    break;
		if ( i == start )
		{
		    vector->ve[old_i] = tmp;
		    px->pe[old_i] = i+size;
		    break;
		}
	    }
	    start++;
	}
	
	for ( i = 0; i < size; i++ )
	    if ( px->pe[i] < size )
		error(E_BOUNDS,"px_vec");
	    else
		px->pe[i] = px->pe[i]-size;
    }
    
    return out;
}

/* pxinv_zvec -- apply the inverse of px to x, returning the result in out
		-- may NOT be in situ */
ZVEC	*pxinv_zvec(px,x,out)
PERM	*px;
ZVEC	*x, *out;
{
    u_int	i, size;
    
    if ( ! px || ! x )
	error(E_NULL,"pxinv_zvec");
    if ( px->size > x->dim )
	error(E_SIZES,"pxinv_zvec");
    if ( ! out || out->dim < x->dim )
	out = zv_resize(out,x->dim);
    
    size = px->size;
    if ( size == 0 )
	return zv_copy(x,out);
    if ( out != x )
    {
	for ( i=0; i<size; i++ )
	    if ( px->pe[i] >= size )
		error(E_BOUNDS,"pxinv_vec");
	    else
		out->ve[px->pe[i]] = x->ve[i];
    }
    else
    {	/* in situ algorithm --- cheat's way out */
	px_inv(px,px);
	px_zvec(px,x,out);
	px_inv(px,px);
    }
    
    
    return out;
}

/* zv_rand -- randomise a complex vector; uniform in [0,1)+[0,1)*i */
ZVEC	*zv_rand(x)
ZVEC	*x;
{
    if ( ! x )
	error(E_NULL,"zv_rand");

    mrandlist((Real *)(x->ve),2*x->dim);

    return x;
}
