/*
 *
 * 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: cartcreate.c,v 6.2 1999/05/25 22:07:44 kmeyer1 Exp $
 *
 *	Function:	- create a communicator with cartesian mapping
 *	Accepts:	- old communicator
 *			- # dimensions
 *			- # procs per dimension
 *			- dimension periodicity
 *			- rank reordering flag
 *			- ptr new communicator
 *	Returns:	- MPI_SUCCESS or error code
 */

#include <stdlib.h>

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

int
MPI_Cart_create(comm, ndims, dims, periods, reorder, pnewcomm)

MPI_Comm		comm;
int			ndims;
int			*dims;
int			*periods;
int			reorder;
MPI_Comm		*pnewcomm;

{
	MPI_Comm	newcomm;
	MPI_Group	newgroup;
	int		size;
	int		nprocs;
	int		rank;
	int		err;
	int		range[1][3];
	int		i;
	int		*p;

	lam_initerr();
	lam_setfunc(BLKMPICARTCREATE);
/*
 * Check the arguments.
 */
	if (comm == MPI_COMM_NULL) {
		return(lam_errfunc(MPI_COMM_WORLD,
			BLKMPICARTCREATE, lam_mkerr(MPI_ERR_COMM, 0)));
	}

	if (LAM_IS_INTER(comm)) {
		return(lam_errfunc(comm,
			BLKMPICARTCREATE, lam_mkerr(MPI_ERR_COMM, 0)));
	}

	if (ndims < 1) {
		return(lam_errfunc(comm,
			BLKMPICARTCREATE, lam_mkerr(MPI_ERR_DIMS, 0)));
	}

	if ((dims == 0) || (periods == 0) || (pnewcomm == 0)) {
		return(lam_errfunc(comm,
			BLKMPICARTCREATE, lam_mkerr(MPI_ERR_ARG, 0)));
	}

	LAM_TRACE(lam_tr_cffstart(BLKMPICARTCREATE));
/*
 * Compute the # of processes in the grid.
 */
	nprocs = 1;
	for (i = 0, p = dims; i < ndims; ++i, ++p) {
		if (*p <= 0) {
			return(lam_errfunc(comm, BLKMPICARTCREATE,
					lam_mkerr(MPI_ERR_DIMS, 0)));
		}

		nprocs *= *p;
	}
/*
 * Create the group for the new communicator.
 */
	err = MPI_Comm_size(comm, &size);
	if (err != MPI_SUCCESS) {
		return(lam_errfunc(comm, BLKMPICARTCREATE, err));
	}

	if (nprocs > size) {
		return(lam_errfunc(comm, BLKMPICARTCREATE,
				lam_mkerr(MPI_ERR_DIMS, 0)));
	}

	if (nprocs == size) {
		err = MPI_Comm_group(comm, &newgroup);
	} else {
		range[0][0] = 0;
		range[0][1] = nprocs - 1;
		range[0][2] = 1;
		err = MPI_Group_range_incl(comm->c_group, 1, range, &newgroup);
	}

	if (err != MPI_SUCCESS) {
		return(lam_errfunc(comm, BLKMPICARTCREATE, err));
	}
/*
 * Create the new communicator.
 */
	err = MPI_Comm_create(comm, newgroup, pnewcomm);
	if (err != MPI_SUCCESS) {
		MPI_Group_free(&newgroup);
		return(lam_errfunc(comm, BLKMPICARTCREATE, err));
	}
/*
 * Fill the communicator with topology information. 
 */
	newcomm = *pnewcomm;
	if (newcomm != MPI_COMM_NULL) {
		newcomm->c_topo_type = MPI_CART;
		newcomm->c_topo_nprocs = nprocs;
		newcomm->c_topo_ndims = ndims;

		newcomm->c_topo_dims = (int *)
				malloc((unsigned) 2 * ndims * sizeof(int));
		if (newcomm->c_topo_dims == 0) {
			return(lam_errfunc(comm, BLKMPICARTCREATE,
					lam_mkerr(MPI_ERR_OTHER, errno)));
		}
		newcomm->c_topo_coords = newcomm->c_topo_dims + ndims;

		for (i = 0, p = newcomm->c_topo_dims; i < ndims; ++i, ++p) {
			*p = (*periods) ? -(*dims) : *dims;
			++dims;
			++periods;
		}
/*
 * Compute the caller's coordinates.
 */
		err = MPI_Comm_rank(newcomm, &rank);
		if (err != MPI_SUCCESS) {
			return(lam_errfunc(comm, BLKMPICARTCREATE, err));
		}

		err = MPI_Cart_coords(newcomm, rank,
					ndims, newcomm->c_topo_coords);
		if (err != MPI_SUCCESS) {
			return(lam_errfunc(comm, BLKMPICARTCREATE, err));
		}
	}

	err = MPI_Group_free(&newgroup);
	if (err != MPI_SUCCESS) {
		return(lam_errfunc(comm, BLKMPICARTCREATE, err));
	}

	LAM_TRACE(lam_tr_cffend(BLKMPICARTCREATE, -1, comm, 0, 0));

	lam_resetfunc(BLKMPICARTCREATE);
	return(MPI_SUCCESS);
}
