/*
 * File         : MPI.java
 * Author       : Sang Lim, Sung-Hoon Ko, Xinying Li, Bryan Carpenter
 *                (contributions from MAEDA Atusi)
 * Created      : Thu Apr  9 12:22:15 1998
 * Revision     : $Revision: 1.12 $
 * Updated      : $Date: 1999/09/14 20:50:15 $
 * Copyright: Northeast Parallel Architectures Center
 *            at Syracuse University 1998
 */

package mpi;

public class MPI {

  static int MAX_PROCESSOR_NAME = 256;

  static public Intracomm COMM_WORLD;
  static public Comm COMM_SELF;

  static public int GRAPH, CART;
  static public int ANY_SOURCE, ANY_TAG;

  static public Op MAX, MIN, SUM, PROD, LAND, BAND,
                   LOR, BOR, LXOR, BXOR, MINLOC, MAXLOC;

  static public Datatype BYTE, CHAR, SHORT, BOOLEAN, 
    INT, LONG, FLOAT, DOUBLE, PACKED, LB, UB, OBJECT;
  static public Datatype SHORT2, INT2, LONG2, FLOAT2, DOUBLE2;

  static public Request REQUEST_NULL;
  static public Group GROUP_EMPTY;

  static public int PROC_NULL;
  static public int BSEND_OVERHEAD;
  static public int UNDEFINED;
  static public int IDENT, CONGRUENT, SIMILAR, UNEQUAL;
  static public int TAG_UB, HOST, IO;

  static Errhandler ERRORS_ARE_FATAL, ERRORS_RETURN;

  /**
   * Initialize MPI.
   * <p>
   * <table>
   * <tr><td><tt> args </tt></td><td> arguments to <tt>main</tt> method. </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_INIT</tt>.
   */

  static public String [] Init(String[] args) throws MPIException {
    String [] newArgs = InitNative(args);

    BYTE    = new Datatype(1);
    CHAR    = new Datatype(2);
    SHORT   = new Datatype(3);
    BOOLEAN = new Datatype(4);
    INT     = new Datatype(5);
    LONG    = new Datatype(6);
    FLOAT   = new Datatype(7);
    DOUBLE  = new Datatype(8);
    PACKED  = new Datatype(9);
    LB      = new Datatype(10);
    UB      = new Datatype(11);
    OBJECT  = new Datatype(12);

    SHORT2  = Datatype.Contiguous(2, MPI.SHORT);
    INT2    = Datatype.Contiguous(2, MPI.INT);
    LONG2   = Datatype.Contiguous(2, MPI.LONG);
    FLOAT2  = Datatype.Contiguous(2, MPI.FLOAT);
    DOUBLE2 = Datatype.Contiguous(2, MPI.DOUBLE);

    SHORT2.Commit();
    INT2.Commit();
    LONG2.Commit();
    FLOAT2.Commit();
    DOUBLE2.Commit();

    MAX  = new Op(1);
    MIN  = new Op(2);
    SUM  = new Op(3);
    PROD = new Op(4);
    LAND = new Op(5);
    BAND = new Op(6);
    LOR  = new Op(7);
    BOR  = new Op(8);
    LXOR = new Op(9);
    BXOR = new Op(10);

    MINLOC = new Op(new Minloc(), true);
    MAXLOC = new Op(new Maxloc(), true);

    GROUP_EMPTY  = new Group(Group.EMPTY);
    REQUEST_NULL = new Request(Request.NULL);
    
    // Constant
    SetConstant(); 

    ERRORS_ARE_FATAL = new Errhandler(Errhandler.FATAL);
    ERRORS_RETURN    = new Errhandler(Errhandler.RETURN);       

    COMM_WORLD = new Intracomm(Intracomm.WORLD);

    return newArgs ;
  }

  static private native String [] InitNative(String[] args);
  static private native void SetConstant();

  /**
   * Finalize MPI.
   * <p>
   * Java binding of the MPI operation <tt>MPI_FINALIZE</tt>.
   */

  static public native void Finalize() throws MPIException ;

  /**
   * Returns wallclock time.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> elapsed wallclock time in seconds
   *                                      since some time in the past </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_WTIME</tt>.
   */

  static public native double Wtime();

  /**
   * Returns resolution of timer.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> resolution of <tt>wtime</tt> in
   *                                      seconds. </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_WTICK</tt>.
   */

  static public native double Wtick();

  /**
   * Returns the name of the processor on which it is called.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> A unique specifier for the actual
   *                                      node. </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_GET_PROCESSOR_NAME</tt>.
   */

  static public String Get_processor_name() throws MPIException {
    byte[] buf = new byte[MAX_PROCESSOR_NAME] ;
    int lengh = Get_processor_name(buf) ;
    return new String(buf,0,lengh) ;
  }

  static private native int Get_processor_name(byte[] buf) ;

  /**
   * Test if MPI has been initialized.
   * <p>
   * <table>
   * <tr><td><em> returns: </em></td><td> <tt>true</tt> if <tt>Init</tt> has
   *                                      been called, <tt>false</tt>
   *                                      otherwise. </tr>
   * </table>
   * <p>
   * Java binding of the MPI operation <tt>MPI_INITIALIZED</tt>.
   */

  static public native boolean Initialized() throws MPIException ;

  // Buffer allocation

  private static byte [] buffer = null ;

  /**
   * Provides to MPI a buffer in user's memory to be used for buffering
   * outgoing messages.
   * Java binding of the MPI operation <tt>MPI_BUFFER_ATTACH</tt>.
   */

  static public void Buffer_attach(byte[] buffer) throws MPIException {
    MPI.buffer = buffer ;
    Buffer_attach_native(buffer);
  }

  static private native void Buffer_attach_native(byte[] buffer);


  /**
   * Detach the buffer currently associated with MPI.
   * Java binding of the MPI operation <tt>MPI_BUFFER_DETACH</tt>.
   */

  static public byte[] Buffer_detach() throws MPIException {
    Buffer_detach_native(buffer);
    byte [] result = MPI.buffer ;
    MPI.buffer = null ;
    return result ;
  }

  static private native void Buffer_detach_native(byte[] buffer);
   
  static {
    System.loadLibrary("mpijava");
  }
}


// Minloc and Maxloc

class Maxloc extends User_function{
  public void Call(Object invec, int inoffset, Object outvec, int outoffset,
                   int count, Datatype datatype){
    if(datatype == MPI.SHORT2) {
      short [] in_array = (short[])invec;
      short [] out_array = (short[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2) {  
        short inval  = in_array  [indisp] ;
        short outval = out_array [outdisp] ;

        if(inval > outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          short inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.INT2) {
      int [] in_array = (int[])invec;
      int [] out_array = (int[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        int inval  = in_array  [indisp] ;
        int outval = out_array [outdisp] ;

        if(inval > outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          int inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.LONG2) {
      long [] in_array = (long[])invec;
      long [] out_array = (long[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        long inval  = in_array  [indisp] ;
        long outval = out_array [outdisp] ;

        if(inval > outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          long inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.FLOAT2) {
      float [] in_array = (float[])invec;
      float [] out_array = (float[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        float inval  = in_array  [indisp] ;
        float outval = out_array [outdisp] ;

        if(inval > outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          float inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.DOUBLE2) {
      double [] in_array = (double[])invec;
      double [] out_array = (double[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        double inval  = in_array  [indisp] ;
        double outval = out_array [outdisp] ;

        if(inval > outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          double inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else {
      System.out.println("MPI.MAXLOC: invalid datatype") ;
      try {
        MPI.COMM_WORLD.Abort(1);
      }
      catch(MPIException e) {}
    }
  }
}

class Minloc extends User_function{
  public void Call(Object invec, int inoffset, Object outvec, int outoffset,
                   int count, Datatype datatype){
    if(datatype == MPI.SHORT2) {
      short [] in_array = (short[])invec;
      short [] out_array = (short[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        short inval  = in_array  [indisp] ;
        short outval = out_array [outdisp] ;

        if(inval < outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          short inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.INT2) {
      int [] in_array = (int[])invec;
      int [] out_array = (int[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        int inval  = in_array  [indisp] ;
        int outval = out_array [outdisp] ;

        if(inval < outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          int inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.LONG2) {
      long [] in_array = (long[])invec;
      long [] out_array = (long[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        long inval  = in_array  [indisp] ;
        long outval = out_array [outdisp] ;

        if(inval < outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          long inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.FLOAT2) {
      float [] in_array = (float[])invec;
      float [] out_array = (float[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        float inval  = in_array  [indisp] ;
        float outval = out_array [outdisp] ;

        if(inval < outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          float inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else if(datatype == MPI.DOUBLE2) {
      double [] in_array = (double[])invec;
      double [] out_array = (double[])outvec;

      int indisp  = inoffset ;
      int outdisp = outoffset ;
      for (int i = 0; i < count; i++, indisp += 2, outdisp += 2){
        
        double inval  = in_array  [indisp] ;
        double outval = out_array [outdisp] ;

        if(inval < outval) {
          out_array [outdisp    ] = inval ;
          out_array [outdisp + 1] = in_array [outdisp + 1] ;
        }
        else if(inval == outval) {
          double inloc = in_array [indisp + 1] ;

          if(inloc < out_array [outdisp + 1])
            out_array [outdisp + 1] = inloc ;
        }
      }
    }
    else {
      System.out.println("MPI.MINLOC: invalid datatype") ;
      try {
        MPI.COMM_WORLD.Abort(1);
      }
      catch(MPIException e) {}
    }
  }
}

// Things to do:
//

