/*
 *
 * 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 1997 The Ohio State University
 *	RBD/NJN
 *
 *	$Id: graphcreate.c,v 6.3 1999/05/25 22:07:56 kmeyer1 Exp $
 *
 *	Function:	- create a communicator with graph mapping
 *	Accepts:	- old communicator
 *			- # nodes
 *			- node index array
 *			- array of edges
 *			- rank reordering flag
 *			- new communicator (out)
 *	Returns:	- MPI_SUCCESS or error code
 */

#include <stdlib.h>

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

int
MPI_Graph_create(comm, nnodes, index, edges, reorder, newcomm)

MPI_Comm		comm;
int			nnodes;
int			*index;
int			*edges;
int			reorder;
MPI_Comm		*newcomm;

{
	MPI_Group	newgroup;
	int		nedges;
	int		size;
	int		err;
	int		range[1][3];
	int		i;
	int		*topo;
	int		*p;

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

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

	if ((nnodes < 1) || (index == 0) || (edges == 0) || (newcomm == 0)) {
		return(lam_errfunc(comm,
			BLKMPIGRAPHCREATE, lam_mkerr(MPI_ERR_ARG, 0)));
	}

	LAM_TRACE(lam_tr_cffstart(BLKMPIGRAPHCREATE));
/*
 * Create and error check the topology information.
 */
	nedges = index[nnodes - 1];

	topo = (int *) malloc((unsigned) (nnodes + nedges) * sizeof(int));
	if (topo == 0) {
		return(lam_errfunc(comm, BLKMPIGRAPHCREATE,
					lam_mkerr(MPI_ERR_OTHER, errno)));
	}

	for (i = 0, p = topo; i < nnodes; ++i, ++p) {
		*p = *index++;
	}

	for (i = 0; i < nedges; ++i, ++p) {
		*p = *edges++;
		if (*p < 0 || *p >= nnodes) {
			free((char *) topo);
			return(lam_errfunc(comm, BLKMPIGRAPHCREATE,
					lam_mkerr(MPI_ERR_TOPOLOGY, 0)));
		}
	}
/*
 * Create the group for the new communicator.
 */
	err = MPI_Comm_size(comm, &size);
	if (err != MPI_SUCCESS) {
		free((char *) topo);
		return(lam_errfunc(comm, BLKMPIGRAPHCREATE, err));
	}

	if (nnodes > size) {
		free((char *) topo);
		return(lam_errfunc(comm, BLKMPIGRAPHCREATE,
				lam_mkerr(MPI_ERR_ARG, 0)));
	}

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

	if (err != MPI_SUCCESS) {
		free((char *) topo);
		return(lam_errfunc(comm, BLKMPIGRAPHCREATE, err));
	}
/*
 * Create the new communicator.
 */
	err = MPI_Comm_create(comm, newgroup, newcomm);
	if (err != MPI_SUCCESS) {
		free((char *) topo);
		MPI_Group_free(&newgroup);
		return(lam_errfunc(comm, BLKMPIGRAPHCREATE, err));
	}
/*
 * Set the communicator topology information. 
 */
	if (*newcomm != MPI_COMM_NULL) {
		(*newcomm)->c_topo_type = MPI_GRAPH;
		(*newcomm)->c_topo_nprocs = nnodes;
		(*newcomm)->c_topo_nedges = nedges;
		(*newcomm)->c_topo_index = topo;
		(*newcomm)->c_topo_edges = topo + nnodes;
	}

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

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

	lam_resetfunc(BLKMPIGRAPHCREATE);
	return(MPI_SUCCESS);
}
