/*
 * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2005 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2015      Los Alamos National Security, LLC. All rights
 *                         reserved.
 * Copyright (c) 2018      FUJITSU LIMITED.  All rights reserved.
 * Copyright (c) 2022      Cisco Systems, Inc.  All rights reserved
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 *
 *
 * This file is almost a complete re-write for Open MPI compared to the
 * original mpiJava package. Its license and copyright are listed below.
 * See <path to ompi/mpi/java/README> for more information.
 *
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 * File         : Intracommm.java
 * Author       : Sang Lim, Xinying Li, Bryan Carpenter
 * Created      : Thu Apr  9 12:22:15 1998
 * Revision     : $Revision: 1.14 $
 * Updated      : $Date: 2002/12/16 15:25:13 $
 * Copyright: Northeast Parallel Architectures Center
 *            at Syracuse University 1998
 *
 *
 *
 * IMPLEMENTATION DETAILS
 *
 * All methods with buffers that can be direct or non direct have
 * a companion argument 'db' which is true if the buffer is direct.
 * For example, if the buffer argument is recvBuf, the companion
 * argument will be 'rdb', meaning if the receive buffer is direct.
 *
 * Checking if a buffer is direct is faster in Java than C.
 */
package mpi;

import java.nio.*;
import static mpi.MPI.assertDirectBuffer;

/**
 * This class represents intracommunicator.
 */
public class Intracomm extends Comm
{
	protected Intracomm()
	{
	}

	protected Intracomm(long handle)
	{
		super(handle);
	}

	protected Intracomm(long[] commRequest)
	{
		super(commRequest);
	}

	/**
	 * Duplicates this communicator.
	 * <p>Java binding of {@code MPI_COMM_DUP}.
	 * <p>It is recommended to use {@link #dup} instead of {@link #clone}
	 * because the last can't throw an {@link mpi.MPIException}.
	 * @return copy of this communicator
	 */
	@Override public Intracomm clone()
	{
		try
		{
			return dup();
		}
		catch(MPIException e)
		{
			throw new RuntimeException(e.getMessage());
		}
	}

	/**
	 * Duplicates this communicator.
	 * <p>Java binding of {@code MPI_COMM_DUP}.
	 * @return copy of this communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	@Override public Intracomm dup() throws MPIException
	{
		MPI.check();
		return new Intracomm(dup(handle));
	}

	/**
	 * Duplicates this communicator.
	 * <p>Java binding of {@code MPI_COMM_IDUP}.
	 * <p>The new communicator can't be used before the operation completes.
	 * The request object must be obtained calling {@link #getRequest}.
	 * @return copy of this communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	@Override public Intracomm iDup() throws MPIException
	{
		MPI.check();
		return new Intracomm(iDup(handle));
	}

	/**
	 * Duplicates this communicator with the info object used in the call.
	 * <p>Java binding of {@code MPI_COMM_DUP_WITH_INFO}.
	 * @param info	info object to associate with the new communicator
	 * @return copy of this communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	@Override public Intracomm dupWithInfo(Info info) throws MPIException
	{
	    MPI.check();
	    return new Intracomm(dupWithInfo(handle, info.handle));
	}

	/**
	 * Partition the group associated with this communicator and create
	 * a new communicator within each subgroup.
	 * <p>Java binding of the MPI operation {@code MPI_COMM_SPLIT}.
	 * @param colour control of subset assignment
	 * @param key    control of rank assignment
	 * @return new communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intracomm split(int colour, int key) throws MPIException
	{
		MPI.check();
		return new Intracomm(split(handle, colour, key));
	}

	private native long split(long comm, int colour, int key) throws MPIException;

	/**
	 * Partition the group associated with this communicator and create
	 * a new communicator within each subgroup.
	 * <p>Java binding of the MPI operation {@code MPI_COMM_SPLIT_TYPE}.
	 * @param splitType	type of processes to be grouped together
	 * @param key	    control of rank assignment
	 * @param info		info argument
	 * @return new communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intracomm splitType(int splitType, int key, Info info) throws MPIException
	{
		MPI.check();
		return new Intracomm(splitType(handle, splitType, key, info.handle));
	}

	private native long splitType(long comm, int colour, int key, long info) throws MPIException;

	/**
	 * Create a new communicator.
	 * <p>Java binding of the MPI operation {@code MPI_COMM_CREATE}.
	 * @param group group which is a subset of the group of this communicator
	 * @return new communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intracomm create(Group group) throws MPIException
	{
		MPI.check();
		return new Intracomm(create(handle, group.handle));
	}

	private native long create(long comm, long group);

	/**
	 * Create a new intracommunicator for the given group.
	 * <p>Java binding of the MPI operation {@code MPI_COMM_CREATE_GROUP}.
	 * @param group group which is a subset of the group of this communicator
	 * @param tag	an integer tag
	 * @return new communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intracomm createGroup(Group group, int tag) throws MPIException
	{
	    MPI.check();
	    return new Intracomm(createGroup(handle, group.handle, tag));
	}

	private native long createGroup(long comm, long group, int tag);

	// Topology Constructors

	/**
	 * Creates a communicator to which the Cartesian topology
	 * information is attached.
	 * Create a cartesian topology communicator whose group is a subset
	 * of the group of this communicator.
	 * <p>Java binding of the MPI operation {@code MPI_CART_CREATE}.
	 * <p>The number of dimensions of the Cartesian grid is taken to be the
	 * size of the {@code dims} argument. The array {@code periods} must
	 * be the same size.
	 * @param dims    the number of processes in each dimension
	 * @param periods {@code true}  if grid is periodic,
	 *                {@code false} if not, in each dimension
	 * @param reorder {@code true}  if ranking may be reordered,
	 *                {@code false} if not
	 * @return new cartesian topology communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final CartComm createCart(int[] dims, boolean[] periods, boolean reorder)
			throws MPIException
	{
		MPI.check();
		return new CartComm(createCart(handle, dims, periods, reorder));
	}

	private native long createCart(
			long comm, int[] dims, boolean[] periods, boolean reorder)
					throws MPIException;

	/**
	 * Creates a communicator to which the graph topology information is attached.
	 * <p>Java binding of the MPI operation {@code MPI_GRAPH_CREATE}.
	 * <p>The number of nodes in the graph, <em>nnodes</em>, is taken
	 * to be size of the {@code index} argument.
	 * @param index   node degrees
	 * @param edges   graph edges
	 * @param reorder {@code true} if ranking may be reordered,
	 *                {@code false} if not
	 * @return new graph topology communicator
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final GraphComm createGraph(int[] index, int[] edges, boolean reorder)
			throws MPIException
	{
		MPI.check();
		return new GraphComm(createGraph(handle, index, edges, reorder));
	}

	private native long createGraph(
			long comm, int[] index, int[] edges, boolean reorder)
					throws MPIException;

	/**
	 * Creates a communicator to which the distributed graph topology
	 * information is attached.
	 * <p>Java binding of the MPI operation {@code MPI_DIST_GRAPH_CREATE}.
	 * <p>The number of source nodes is the size of the {@code sources} argument.
	 * @param sources      source nodes for which this process specifies edges
	 * @param degrees      number of destinations for each source node
	 * @param destinations destination nodes for the source nodes
	 * @param weights      weights for source to destination edges
	 * @param info         hints on optimization and interpretation of weights
	 * @param reorder      the process may be reordered (true) or not (false)
	 * @return communicator with distributed graph topology
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final GraphComm createDistGraph(
			int[] sources, int[] degrees, int[] destinations,
			int[] weights, Info info, boolean reorder)
					throws MPIException
	{
		MPI.check();

		return new GraphComm(createDistGraph(
				handle, sources, degrees, destinations,
				weights, info.handle, reorder, true));
	}

	/**
	 * Creates a communicator to which the distributed graph topology
	 * information is attached.
	 * <p>Java binding of the MPI operation {@code MPI_DIST_GRAPH_CREATE}
	 * using {@code MPI_UNWEIGHTED}.
	 * <p>The number of source nodes is the size of the {@code sources} argument.
	 * @param sources      source nodes for which this process specifies edges
	 * @param degrees      number of destinations for each source node
	 * @param destinations destination nodes for the source nodes
	 * @param info         hints on optimization and interpretation of weights
	 * @param reorder      the process may be reordered (true) or not (false)
	 * @return communicator with distributed graph topology
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final GraphComm createDistGraph(
			int[] sources, int[] degrees, int[] destinations,
			Info info, boolean reorder)
					throws MPIException
	{
		MPI.check();

		return new GraphComm(createDistGraph(
				handle, sources, degrees, destinations,
				null, info.handle, reorder, false));
	}

	private native long createDistGraph(
			long comm, int[] sources, int[] degrees, int[] destinations,
			int[] weights, long info, boolean reorder, boolean weighted)
					throws MPIException;


	/**
	 * Creates a communicator to which the distributed graph topology
	 * information is attached.
	 * <p>Java binding of the MPI operation {@code MPI_DIST_GRAPH_CREATE_ADJACENT}.
	 * <p>The number of source/destination nodes is the size of the
	 * {@code sources}/{@code destinations} argument.
	 * @param sources       ranks of processes for which the calling process
	 *                      is a destination
	 * @param sourceWeights weights of the edges into the calling process
	 * @param destinations  ranks of processes for which the calling process
	 *                      is a source
	 * @param destWeights   weights of the edges out of the calling process
	 * @param info          hints on optimization and interpretation of weights
	 * @param reorder       the process may be reordered (true) or not (false)
	 * @return communicator with distributed graph topology
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final GraphComm createDistGraphAdjacent(
			int[] sources, int[] sourceWeights,
			int[] destinations, int[] destWeights, Info info, boolean reorder)
					throws MPIException
	{
		MPI.check();

		return new GraphComm(createDistGraphAdjacent(
				handle, sources, sourceWeights, destinations,
				destWeights, info.handle, reorder, true));
	}

	/**
	 * Creates a communicator to which the distributed graph topology
	 * information is attached.
	 * <p>Java binding of the MPI operation {@code MPI_DIST_GRAPH_CREATE_ADJACENT}
	 * using {@code MPI_UNWEIGHTED}.
	 * <p>The number of source/destination nodes is the size of the
	 * {@code sources}/{@code destinations} argument.
	 * @param sources      ranks of processes for which the calling process
	 *                     is a destination
	 * @param destinations ranks of processes for which the calling process
	 *                     is a source
	 * @param info         hints on optimization and interpretation of weights
	 * @param reorder      the process may be reordered (true) or not (false)
	 * @return communicator with distributed graph topology
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final GraphComm createDistGraphAdjacent(
			int[] sources, int[] destinations, Info info, boolean reorder)
					throws MPIException
	{
		MPI.check();

		return new GraphComm(createDistGraphAdjacent(
				handle, sources, null, destinations, null,
				info.handle, reorder, false));
	}

	private native long createDistGraphAdjacent(
			long comm, int[] sources, int []sourceweights, int[] destinations,
			int[] distweights, long info, boolean reorder, boolean weighted)
					throws MPIException;


	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_SCAN}.
	 * @param sendbuf send buffer array
	 * @param recvbuf receive buffer array
	 * @param count   number of items in input buffer
	 * @param type    data type of each item in input buffer
	 * @param op      reduce operation
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final void scan(Object sendbuf, Object recvbuf,
			int count, Datatype type, Op op)
					throws MPIException
	{
		MPI.check();

		int sendoff = 0,
				recvoff = 0;

		boolean sdb = false,
				rdb = false;

		if(sendbuf instanceof Buffer && !(sdb = ((Buffer)sendbuf).isDirect()))
		{
			sendoff = type.getOffset(sendbuf);
			sendbuf = ((Buffer)sendbuf).array();
		}

		if(recvbuf instanceof Buffer && !(rdb = ((Buffer)recvbuf).isDirect()))
		{
			recvoff = type.getOffset(recvbuf);
			recvbuf = ((Buffer)recvbuf).array();
		}

		op.setDatatype(type);

		scan(handle, sendbuf, sdb, sendoff, recvbuf, rdb, recvoff,
				count, type.handle, type.baseType, op, op.handle);
	}

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_SCAN}
	 * using {@code MPI_IN_PLACE} instead of the send buffer.
	 * @param recvbuf receive buffer array
	 * @param count   number of items in input buffer
	 * @param type    data type of each item in input buffer
	 * @param op      reduce operation
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final void scan(Object recvbuf, int count, Datatype type, Op op)
			throws MPIException
	{
		MPI.check();
		int recvoff = 0;
		boolean rdb = false;

		if(recvbuf instanceof Buffer && !(rdb = ((Buffer)recvbuf).isDirect()))
		{
			recvoff = type.getOffset(recvbuf);
			recvbuf = ((Buffer)recvbuf).array();
		}

		op.setDatatype(type);

		scan(handle, null, false, 0, recvbuf, rdb, recvoff,
				count, type.handle, type.baseType, op, op.handle);
	}

	private native void scan(
			long comm, Object sendbuf, boolean sdb, int sendoff,
			Object recvbuf, boolean rdb, int recvoff, int count,
			long type, int baseType, Op jOp, long hOp) throws MPIException;

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_ISCAN}.
	 * @param sendbuf send buffer array
	 * @param recvbuf receive buffer array
	 * @param count   number of items in input buffer
	 * @param type    data type of each item in input buffer
	 * @param op      reduce operation
	 * @return communication request
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Request iScan(Buffer sendbuf, Buffer recvbuf,
			int count, Datatype type, Op op)
					throws MPIException
	{
		MPI.check();
		op.setDatatype(type);
		assertDirectBuffer(sendbuf, recvbuf);
		Request req = new Request(iScan(handle, sendbuf, recvbuf, count,
				type.handle, type.baseType, op, op.handle));
		req.addSendBufRef(sendbuf);
		req.addRecvBufRef(recvbuf);
		return req;
	}

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_ISCAN}
	 * using {@code MPI_IN_PLACE} instead of the send buffer.
	 * @param buf   send/receive buffer array
	 * @param count number of items in buffer
	 * @param type  data type of each item in buffer
	 * @param op    reduce operation
	 * @return communication request
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Request iScan(Buffer buf, int count, Datatype type, Op op)
			throws MPIException
	{
		MPI.check();
		op.setDatatype(type);
		assertDirectBuffer(buf);
		Request req = new Request(iScan(
				handle, null, buf, count,
				type.handle, type.baseType, op, op.handle));
		req.addSendBufRef(buf);
		return req;
	}

	private native long iScan(
			long comm, Buffer sendbuf, Buffer recvbuf, int count,
			long type, int baseType, Op jOp, long hOp) throws MPIException;

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_EXSCAN}.
	 * @param sendbuf send buffer array
	 * @param recvbuf receive buffer array
	 * @param count   number of items in input buffer
	 * @param type    data type of each item in input buffer
	 * @param op      reduce operation
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final void exScan(Object sendbuf, Object recvbuf,
			int count, Datatype type, Op op)
					throws MPIException
	{
		MPI.check();

		int sendoff = 0,
				recvoff = 0;

		boolean sdb = false,
				rdb = false;

		if(sendbuf instanceof Buffer && !(sdb = ((Buffer)sendbuf).isDirect()))
		{
			sendoff = type.getOffset(sendbuf);
			sendbuf = ((Buffer)sendbuf).array();
		}

		if(recvbuf instanceof Buffer && !(rdb = ((Buffer)recvbuf).isDirect()))
		{
			recvoff = type.getOffset(recvbuf);
			recvbuf = ((Buffer)recvbuf).array();
		}

		op.setDatatype(type);

		exScan(handle, sendbuf, sdb, sendoff, recvbuf, rdb, recvoff,
				count, type.handle, type.baseType, op, op.handle);
	}

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_EXSCAN}
	 * using {@code MPI_IN_PLACE} instead of the send buffer.
	 * @param buf   receive buffer array
	 * @param count number of items in input buffer
	 * @param type  data type of each item in input buffer
	 * @param op    reduce operation
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final void exScan(Object buf, int count, Datatype type, Op op)
			throws MPIException
	{
		MPI.check();
		int off = 0;
		boolean db = false;

		if(buf instanceof Buffer && !(db = ((Buffer)buf).isDirect()))
		{
			off = type.getOffset(buf);
			buf = ((Buffer)buf).array();
		}

		op.setDatatype(type);

		exScan(handle, null, false, 0, buf, db, off, count,
				type.handle, type.baseType, op, op.handle);
	}

	private native void exScan(
			long comm, Object sendbuf, boolean sdb, int sendoff,
			Object recvbuf, boolean rdb, int recvoff, int count,
			long type, int baseType, Op jOp, long hOp) throws MPIException;

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_IEXSCAN}.
	 * @param sendbuf send buffer array
	 * @param recvbuf receive buffer array
	 * @param count   number of items in input buffer
	 * @param type    data type of each item in input buffer
	 * @param op      reduce operation
	 * @return communication request
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Request iExScan(Buffer sendbuf, Buffer recvbuf,
			int count, Datatype type, Op op)
					throws MPIException
	{
		MPI.check();
		op.setDatatype(type);
		assertDirectBuffer(sendbuf, recvbuf);
		Request req = new Request(iExScan(handle, sendbuf, recvbuf, count,
				type.handle, type.baseType, op, op.handle));
		req.addSendBufRef(sendbuf);
		req.addRecvBufRef(recvbuf);
		return req;
	}

	/**
	 * Perform a prefix reduction on data distributed across the group.
	 * <p>Java binding of the MPI operation {@code MPI_IEXSCAN}
	 * using {@code MPI_IN_PLACE} instead of the send buffer.
	 * @param buf   receive buffer array
	 * @param count number of items in input buffer
	 * @param type  data type of each item in input buffer
	 * @param op    reduce operation
	 * @return communication request
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Request iExScan(Buffer buf, int count, Datatype type, Op op)
			throws MPIException
	{
		MPI.check();
		op.setDatatype(type);
		assertDirectBuffer(buf);
		Request req = new Request(iExScan(
				handle, null, buf, count,
				type.handle, type.baseType, op, op.handle));
		req.addRecvBufRef(buf);
		return req;
	}

	private native long iExScan(
			long comm, Buffer sendbuf, Buffer recvbuf, int count,
			long type, int baseType, Op jOp, long hOp) throws MPIException;

	/**
	 * Java binding of {@code MPI_OPEN_PORT} using {@code MPI_INFO_NULL}.
	 * @return port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static String openPort() throws MPIException
	{
		MPI.check();
		return openPort(Info.NULL);
	}

	/**
	 * Java binding of {@code MPI_OPEN_PORT}.
	 * @param info implementation-specific information
	 * @return port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static String openPort(Info info) throws MPIException
	{
		MPI.check();
		return openPort(info.handle);
	}

	private native static String openPort(long info) throws MPIException;

	/**
	 * Java binding of {@code MPI_CLOSE_PORT}.
	 * @param name port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static void closePort(String name) throws MPIException
	{
		MPI.check();
		closePort_jni(name);
	}

	private native static void closePort_jni(String name) throws MPIException;

	/**
	 * Java binding of {@code MPI_COMM_ACCEPT} using {@code MPI_INFO_NULL}.
	 * @param port port name
	 * @param root rank in comm of root node
	 * @return intercommunicator with client as remote group
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intercomm accept(String port, int root) throws MPIException
	{
		MPI.check();
		return new Intercomm(accept(handle, port, Info.NULL, root));
	}

	/**
	 * Java binding of {@code MPI_COMM_ACCEPT}.
	 * @param port port name
	 * @param info implementation-specific information
	 * @param root rank in comm of root node
	 * @return intercommunicator with client as remote group
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intercomm accept(String port, Info info, int root)
			throws MPIException
	{
		MPI.check();
		return new Intercomm(accept(handle, port, info.handle, root));
	}

	private native long accept(long comm, String port, long info, int root)
			throws MPIException;

	/**
	 * Java binding of {@code MPI_COMM_CONNECT} using {@code MPI_INFO_NULL}.
	 * @param port port name
	 * @param root rank in comm of root node
	 * @return intercommunicator with server as remote group
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intercomm connect(String port, int root) throws MPIException
	{
		MPI.check();
		return new Intercomm(connect(handle, port, Info.NULL, root));
	}

	/**
	 * Java binding of {@code MPI_COMM_CONNECT}.
	 * @param port port name
	 * @param info implementation-specific information
	 * @param root rank in comm of root node
	 * @return intercommunicator with server as remote group
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intercomm connect(String port, Info info, int root)
			throws MPIException
	{
		MPI.check();
		return new Intercomm(connect(handle, port, info.handle, root));
	}

	private native long connect(long comm, String port, long info, int root)
			throws MPIException;

	/**
	 * Java binding of {@code MPI_PUBLISH_NAME} using {@code MPI_INFO_NULL}.
	 * @param service service name
	 * @param port    port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static void publishName(String service, String port)
			throws MPIException
	{
		MPI.check();
		publishName(service, Info.NULL, port);
	}

	/**
	 * Java binding of {@code MPI_PUBLISH_NAME}.
	 * @param service service name
	 * @param info    implementation-specific information
	 * @param port    port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static void publishName(String service, Info info, String port)
			throws MPIException
	{
		MPI.check();
		publishName(service, info.handle, port);
	}

	private native static void publishName(String service, long info, String port)
			throws MPIException;

	/**
	 * Java binding of {@code MPI_UNPUBLISH_NAME} using {@code MPI_INFO_NULL}.
	 * @param service service name
	 * @param port    port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static void unpublishName(String service, String port)
			throws MPIException
	{
		MPI.check();
		unpublishName(service, Info.NULL, port);
	}

	/**
	 * Java binding of {@code MPI_UNPUBLISH_NAME}.
	 * @param service service name
	 * @param info    implementation-specific information
	 * @param port    port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static void unpublishName(String service, Info info, String port)
			throws MPIException
	{
		MPI.check();
		unpublishName(service, info.handle, port);
	}

	private native static void unpublishName(String service, long info, String port)
			throws MPIException;

	/**
	 * Java binding of {@code MPI_LOOKUP_NAME} using {@code MPI_INFO_NULL}.
	 * @param service service name
	 * @return port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static String lookupName(String service) throws MPIException
	{
		MPI.check();
		return lookupName(service, Info.NULL);
	}

	/**
	 * Java binding of {@code MPI_LOOKUP_NAME}.
	 * @param service service name
	 * @param info    implementation-specific information
	 * @return port name
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public static String lookupName(String service, Info info) throws MPIException
	{
		MPI.check();
		return lookupName(service, info.handle);
	}

	private native static String lookupName(String service, long info)
			throws MPIException;

	/**
	 * Java binding of {@code MPI_COMM_SPAWN}.
	 * This intracommunicator will contain the group of spawned processes.
	 * @param command  name of program to be spawned
	 * @param argv     arguments to command; if this parameter is null,
	 *                 {@code MPI_ARGV_NULL} will be used.
	 * @param maxprocs maximum number of processes to start
	 * @param info     info object telling the runtime where
	 *                 and how to start the processes
	 * @param root     rank of process in which previous arguments are examined
	 * @param errcodes one code per process; if this parameter is null,
	 *                 {@code MPI_ERRCODES_IGNORE} will be used.
	 * @return intercommunicator between original group and the newly spawned group
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intercomm spawn(String command, String[] argv, int maxprocs,
			Info info, int root, int[] errcodes)
					throws MPIException
	{
		MPI.check();

		return new Intercomm(spawn(handle, command, argv, maxprocs,
				info.handle, root, errcodes));
	}

	private native long spawn(long comm, String command, String[] argv,
			int maxprocs, long info, int root, int[] errcodes)
					throws MPIException;

	/**
	 * Java binding of {@code MPI_COMM_SPAWN_MULTIPLE}.
	 * This intracommunicator will contain the group of spawned processes.
	 * @param commands programs to be executed
	 * @param argv     arguments for commands; if this parameter is null,
	 *                 {@code MPI_ARGVS_NULL} will be used.
	 * @param maxprocs maximum number of processes to start for each command
	 * @param info     info objects telling the runtime where
	 *                 and how to start the processes
	 * @param root     rank of process in which previous arguments are examined
	 * @param errcodes one code per process; if this parameter is null,
	 *                 {@code MPI_ERRCODES_IGNORE} will be used.
	 * @return intercommunicator between original group and the newly spawned group
	 * @throws MPIException Signals that an MPI error of some sort has occurred.
	 */
	public final Intercomm spawnMultiple(
			String[] commands, String[][] argv, int[] maxprocs,
			Info[] info, int root, int[] errcodes)
					throws MPIException
	{
		MPI.check();

		long hInfo[] = new long[info.length];

		for(int i = 0; i < info.length; i++)
			hInfo[i] = info[i].handle;

		return new Intercomm(spawnMultiple(handle, commands, argv, maxprocs,
				hInfo, root, errcodes));
	}

	private native long spawnMultiple(
			long comm, String[] commands, String[][] argv, int[] maxprocs,
			long[] info, int root, int[] errcodes) throws MPIException;

} // Intracomm
