/*
 *
 * 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: grexcl.c,v 6.3 1999/05/25 22:07:57 kmeyer1 Exp $
 *
 *	Function:	- form a new group by range exclusion
 *	Accepts:	- original group
 *			- # of ranges
 *			- rank ranges
 *			- ptr new group
 *	Returns:	- MPI_SUCCESS or error code
 */

#include <stdlib.h>

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

int
MPI_Group_range_excl(g, n, ranges, png)

MPI_Group		g;
int			n;
int			ranges[][3];
MPI_Group		*png;

{
	int		i, j;
	int		nproc;			/* # processes */
	int		first;			/* range first */
	int		last;			/* range last */
	int		stride;			/* range stride */
	int		end;			/* calculated end of range */
	struct _proc	**pi, **pj;		/* processes in groups */
	MPI_Group	ng;			/* new group */

	lam_initerr_m();
	lam_setfunc_m(BLKMPIGROUPREXCL);
/*
 * Check the arguments.
 */
	if (g == MPI_GROUP_NULL) {
		return(lam_errfunc(MPI_COMM_WORLD,
			BLKMPIGROUPREXCL, lam_mkerr(MPI_ERR_GROUP, 0)));
	}

	if ((png == 0) || (n < 0) || ((n > 0) && (ranges == 0))) {
		return(lam_errfunc(MPI_COMM_WORLD,
			BLKMPIGROUPREXCL, lam_mkerr(MPI_ERR_ARG, 0)));
	}
/*
 * Flag the processes to exclude.
 */
	nproc = g->g_nprocs;
	pi = g->g_procs;

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

		if ((stride = ranges[i][2]) == 0) break;

		first = ranges[i][0];
		last = ranges[i][1];
		end = first + ((last - first) / stride) * stride;
		
		if (stride > 0) {
			if (first < 0 || first > last || end >= g->g_nprocs) {
				break;
			}

			for (j = first; j <= end; j += stride) {

				if (!(pi[j]->p_mode & LAM_PFLAG)) {
					pi[j]->p_mode |= LAM_PFLAG;
					--nproc;
				} else {
					i = -1;
					break;
				}
			}
		}
		else {
			if (first >= g->g_nprocs || first < last || end < 0) {
				break;
			}
		
			for (j = first; j >= end; j += stride) {

				if (!(pi[j]->p_mode & LAM_PFLAG)) {
					pi[j]->p_mode |= LAM_PFLAG;
					--nproc;
				} else {
					i = -1;
					break;
				}
			}
		}

		if (i < 0) break;
	}
/*
 * If there is an error in the rank ranges clean up and return error.
 */
	if (i < n) {
		for (i = 0, pi = g->g_procs; i < g->g_nprocs; ++i, ++pi) {
			(*pi)->p_mode &= ~LAM_PFLAG;
		}

		return(lam_errfunc(MPI_COMM_WORLD, BLKMPIGROUPREXCL,
					lam_mkerr(MPI_ERR_RANK, 0)));
	}
/*
 * Handle the trivial cases.
 */
	if (nproc == g->g_nprocs) {
		*png = g;
		g->g_refcount++;
		lam_resetfunc_m(BLKMPIGROUPREXCL);
		return(MPI_SUCCESS);
	}

	if (nproc == 0) {
		*png = MPI_GROUP_EMPTY;
		MPI_GROUP_EMPTY->g_refcount++;

		for (i = 0, pi = g->g_procs; i < g->g_nprocs; ++i, ++pi) {
			(*pi)->p_mode &= ~LAM_PFLAG;
		}

		lam_resetfunc_m(BLKMPIGROUPREXCL);
		return(MPI_SUCCESS);
	}
/*
 * Allocate a new group.
 */
	ng = (MPI_Group) malloc((unsigned) sizeof(struct _group) +
					(nproc * sizeof(struct _proc *)));
	if (ng == 0) {
		for (i = 0, pi = g->g_procs; i < g->g_nprocs; ++i, ++pi) {
			(*pi)->p_mode &= ~LAM_PFLAG;
		}

		return(lam_errfunc(MPI_COMM_WORLD,
			BLKMPIGROUPREXCL, lam_mkerr(MPI_ERR_OTHER, errno)));
	}

	*png = ng;
	ng->g_nprocs = nproc;
	ng->g_myrank = MPI_UNDEFINED;
	ng->g_refcount = 1;
	ng->g_f77handle = -1;
	ng->g_procs = (struct _proc **) ((char *) ng + sizeof(struct _group));
/*
 * Fill the new group ranks.
 */
	pj = ng->g_procs;

	for (i = 0, pi = g->g_procs; i < g->g_nprocs; ++i, ++pi) {

		if ((*pi)->p_mode & LAM_PFLAG) {
			(*pi)->p_mode &= ~LAM_PFLAG;
		} else {
			*pj = *pi;
			(*pj)->p_refcount++;
			if (*pj == lam_myproc) {
				ng->g_myrank = (int) (pj - ng->g_procs);
			}
			++pj;
		}
	}

	lam_resetfunc_m(BLKMPIGROUPREXCL);
	return(MPI_SUCCESS);
}
