/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1996 The Ohio State University
 *	RBD
 *
 *	$Id: alltoallv.c,v 6.4 1999/08/04 21:43:49 kmeyer1 Exp $
 *
 *	Function:	- send/recv var. len. data from each node to all nodes
 *	Accepts:	- send buffer
 *			- send counts
 *			- send displacements
 *			- send datatype
 *			- recv buffer
 *			- recv counts
 *			- recv displacements
 *			- recv datatype
 *			- communicator
 *	Returns:	- MPI_SUCCESS or an MPI error code
 */

#include <stdlib.h>

#include <lam_config.h>
#include <blktype.h>
#include <mpi.h>
#include <mpisys.h>
#include <terror.h>

/*
 * local functions
 */
static int		c2c_alltoallv();
static int		lamd_alltoallv();


int
MPI_Alltoallv(sbuf, scounts, sdisps, sdtype,
		rbuf, rcounts, rdisps, rdtype, comm)

void			*sbuf;
int			*scounts;
int			*sdisps;
MPI_Datatype		sdtype;
void			*rbuf;
int			*rcounts;
int			*rdisps;
MPI_Datatype		rdtype;
MPI_Comm		comm;

{
/*
 * Check for invalid arguments.
 */
	if ((comm == MPI_COMM_NULL) || LAM_IS_INTER(comm)) {
		return(lam_errfunc(comm, BLKMPIALLTOALLV,
				lam_mkerr(MPI_ERR_COMM, 0)));
	}

	if ((sdtype == MPI_DATATYPE_NULL) || (rdtype == MPI_DATATYPE_NULL)) {
		return(lam_errfunc(comm, BLKMPIALLTOALLV,
				lam_mkerr(MPI_ERR_TYPE, 0)));
	}

	if ((scounts == 0) || (rcounts == 0)) {
		return(lam_errfunc(comm, BLKMPIALLTOALLV,
				lam_mkerr(MPI_ERR_COUNT, 0)));
	}

	if ((sdisps == 0) || (rdisps == 0)) {
		return(lam_errfunc(comm, BLKMPIALLTOALLV,
				lam_mkerr(MPI_ERR_ARG, 0)));
	}

#if LAM_WANT_IMPI
	
	/* Remove this when IMPI collectives are implemented */
	
        if (LAM_IS_IMPI(comm)) {
	  return lam_err_comm(comm, MPI_ERR_COMM, 0, 
			      "Collectives not yet implemented on IMPI communicators");
	}
#endif 

	LAM_TRACE(lam_tr_cffstart(BLKMPIALLTOALLV));

	return(RPI_SPLIT(lamd_alltoallv, c2c_alltoallv,
				(sbuf, scounts, sdisps, sdtype,
				rbuf, rcounts, rdisps, rdtype, comm)));
}


/*
 *	c2c_alltoallv
 *
 *	Function:	- MPI_Alltoallv for the C2C RPI
 *	Accepts:	- same as MPI_Alltoallv
 *	Returns:	- MPI_SUCCESS or an MPI error code
 */
static int
c2c_alltoallv(sbuf, scounts, sdisps, sdtype,
		rbuf, rcounts, rdisps, rdtype, comm)

void			*sbuf;
int			*scounts;
int			*sdisps;
MPI_Datatype		sdtype;
void			*rbuf;
int			*rcounts;
int			*rdisps;
MPI_Datatype		rdtype;
MPI_Comm		comm;

{
	int		i;			/* favourite index */
	int		size;			/* group size */
	int		rank;			/* my rank */
	int		nreqs;			/* # requests */
	int		err;			/* error code */
	char		*psnd;			/* ptr send buffer */
	char		*prcv;			/* ptr recv buffer */
	MPI_Aint	sndextent;		/* send datatype extent */
	MPI_Aint	rcvextent;		/* recv datatype extent */
	MPI_Request	*req;			/* request array */
	MPI_Request	*preq;			/* ptr request */
/*
 * Initialize.
 */
	MPI_Comm_size(comm, &size);
	MPI_Comm_rank(comm, &rank);

	MPI_Type_extent(sdtype, &sndextent);
	MPI_Type_extent(rdtype, &rcvextent);
/*
 * Allocate arrays of requests.
 */
	nreqs = 2 * (size - 1);

	if (nreqs > 0) {
		req = (MPI_Request *) malloc(nreqs * sizeof(MPI_Request));
		if (req == 0) {
			free((char *) req);
			return(lam_errfunc(comm, BLKMPIALLTOALLV,
					lam_mkerr(MPI_ERR_OTHER, errno)));
		}
	} else {
		req = 0;
	}
/*
 * Switch to collective communicator.
 */
	lam_mkcoll(comm);
/*
 * simple optimization
 */
	psnd = ((char *) sbuf) + (sdisps[rank] * sndextent);
	prcv = ((char *) rbuf) + (rdisps[rank] * rcvextent);

	err = lam_dtsndrcv(psnd, scounts[rank], sdtype,
			prcv, rcounts[rank], rdtype, BLKMPIALLTOALLV, comm);

	if (err != MPI_SUCCESS) {
		if (req) free((char *) req);
		lam_mkpt(comm);
		return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
	}
/*
 * If only one process, generate run time trace and we're done.
 */
	if (size == 1) {
		lam_mkpt(comm);
		LAM_TRACE(lam_tr_cffend(BLKMPIALLTOALLV, -1, comm,
				sdtype, 0));

		lam_resetfunc(BLKMPIALLTOALLV);
		return(MPI_SUCCESS);
	}
/*
 * Initiate all send/recv to/from others.
 */
	preq = req;

	for (i = 0; i < size; ++i) {

		if (i == rank) continue;

		prcv = ((char *) rbuf) + (rdisps[i] * rcvextent);

		err = MPI_Recv_init(prcv, rcounts[i], rdtype,
					i, BLKMPIALLTOALLV, comm, preq++);

		if (err != MPI_SUCCESS) {
			free((char *) req);
			lam_mkpt(comm);
			return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
		}
	}

	for (i = 0; i < size; ++i) {

		if (i == rank) continue;

		psnd = ((char *) sbuf) + (sdisps[i] * sndextent);

		err = MPI_Send_init(psnd, scounts[i], sdtype,
					i, BLKMPIALLTOALLV, comm, preq++);

		if (err != MPI_SUCCESS) {
			free((char *) req);
			lam_mkpt(comm);
			return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
		}
	}
/*
 * Start all requests.
 */
	err = MPI_Startall(nreqs, req);

	if (err != MPI_SUCCESS) {
		free((char *) req);
		lam_mkpt(comm);
		return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
	}
/*
 * Wait for them all.
 */
	err = MPI_Waitall(nreqs, req, MPI_STATUSES_IGNORE);

	lam_mkpt(comm);

	if (err != MPI_SUCCESS) {
		free((char *) req);
		return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
	}
/*
 * Free the requests.
 */
	for (i = 0, preq = req; i < nreqs; ++i, ++preq) {

		err = MPI_Request_free(preq);
		if (err != MPI_SUCCESS) {
			free((char *) req);
			return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
		}
	}

	free((char *) req);
	LAM_TRACE(lam_tr_cffend(BLKMPIALLTOALLV, -1, comm, sdtype, 0));

	lam_resetfunc(BLKMPIALLTOALLV);
	return(MPI_SUCCESS);
}

/*
 *	lamd_alltoallv
 *
 *	Function:	- MPI_Alltoallv for the LAMD RPI
 *	Accepts:	- same as MPI_Alltoallv
 *	Returns:	- MPI_SUCCESS or an MPI error code
 */
static int
lamd_alltoallv(sbuf, scounts, sdisps, sdtype,
		rbuf, rcounts, rdisps, rdtype, comm)

void			*sbuf;
int			*scounts;
int			*sdisps;
MPI_Datatype		sdtype;
void			*rbuf;
int			*rcounts;
int			*rdisps;
MPI_Datatype		rdtype;
MPI_Comm		comm;

{
	int		i;			/* favourite index */
	int		size;			/* group size */
	int		rank;			/* my rank */
	int		err;			/* error code */
	char		*psnd;			/* ptr send buffer */
	char		*prcv;			/* ptr recv buffer */
	MPI_Aint	sndextent;		/* send datatype extent */
	MPI_Aint	rcvextent;		/* recv datatype extent */
/*
 * Initialize.
 */
	MPI_Comm_size(comm, &size);
	MPI_Comm_rank(comm, &rank);

	MPI_Type_extent(sdtype, &sndextent);
	MPI_Type_extent(rdtype, &rcvextent);
/*
 * Switch to collective communicator.
 */
	lam_mkcoll(comm);
/*
 * simple optimization
 */
	psnd = ((char *) sbuf) + (sdisps[rank] * sndextent);
	prcv = ((char *) rbuf) + (rdisps[rank] * rcvextent);

	err = lam_dtsndrcv(psnd, scounts[rank], sdtype,
			prcv, rcounts[rank], rdtype, BLKMPIALLTOALLV, comm);

	if (err != MPI_SUCCESS) {
		lam_mkpt(comm);
		return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
	}
/*
 * Do sendrecv's with others if any.
 */
	for (i = 0; i < size; ++i) {

		if (i == rank) continue;

		psnd = ((char *) sbuf) + (sdisps[i] * sndextent);
		prcv = ((char *) rbuf) + (rdisps[i] * rcvextent);
		
		err = MPI_Sendrecv(psnd, scounts[i], sdtype, i,
			BLKMPIALLTOALLV, prcv, rcounts[i], rdtype, i,
			BLKMPIALLTOALLV, comm, MPI_STATUS_IGNORE);

		if (err != MPI_SUCCESS) {
			lam_mkpt(comm);
			return(lam_errfunc(comm, BLKMPIALLTOALLV, err));
		}
	}	

	lam_mkpt(comm);
	LAM_TRACE(lam_tr_cffend(BLKMPIALLTOALLV, -1, comm, sdtype, 0));
	
	lam_resetfunc(BLKMPIALLTOALLV);
	return(MPI_SUCCESS);
}
