text: /* 
   * @HEADER
   *
   * ***********************************************************************
   *
   *  Zoltan Toolkit for Load-balancing, Partitioning, Ordering and Coloring
   *                  Copyright 2012 Sandia Corporation
   *
   * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
   * the U.S. Government retains certain rights in this software.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   * 1. Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   *
   * 3. Neither the name of the Corporation nor the names of the
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * Questions? Contact Karen Devine	kddevin@sandia.gov
   *                    Erik Boman	egboman@sandia.gov
   *
   * ***********************************************************************
   *
   * @HEADER
   */
  
  #include <mpi.h>
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <ctype.h>
  
  #include "dr_const.h"
  #include "dr_externs.h"
  #include "dr_input_const.h"
  #include "dr_util_const.h"
  #include "dr_par_util_const.h"
  #include "dr_err_const.h"
  #include "dr_output_const.h"
  #include "dr_elem_util_const.h"
  #include "dr_maps_const.h"
  #include "ch_input_const.h"
  #include "ch_init_dist_const.h"
  #include "dr_hg_readfile.h"
  #include "dr_compress_const.h"
  
  #ifdef __cplusplus
  /* if C++, define the rest of this header file as extern C */
  extern "C" {
  #endif
  
  #ifndef MAX_STR_LENGTH
  #define MAX_STR_LENGTH 80
  #endif
  
  #define IS_BLANK(c) ((c == '\t') || (c == ' '))
  
  #define COMMENT_CHAR '%'
  
  #define PRINT_DEBUG_INFO 0
  
  #if PRINT_DEBUG_INFO
  static void debug_elements(int Proc, int Num_Proc, int num, ELEM_INFO_PTR el);
  static void debug_lists(int Proc, int Num_Proc, int nedge, int *index, ZOLTAN_ID_TYPE *vtx, 
    int *vtx_proc, ZOLTAN_ID_TYPE *egid);
  static void debug_pins(int Proc, int Num_Proc,  
            ZOLTAN_ID_TYPE nGlobalEdges, ZOLTAN_ID_TYPE nGlobalVtxs, 
            int vtxWDim, int edgeWDim,
            int nMyPins, ZOLTAN_ID_TYPE *myPinI, ZOLTAN_ID_TYPE *myPinJ,
            int nMyVtx, ZOLTAN_ID_TYPE *myVtxNum, float *myVtxWgts,
            int nMyEdgeWgts, ZOLTAN_ID_TYPE *myEWGno, float *myEdgeWgts);
  #endif
  
  static int dist_hyperedges( MPI_Comm, PARIO_INFO_PTR, int, int, int, 
      int *, int *, int **, int **, int **, int **, int *, float  **, short  *);
  
  static int create_edge_lists(int, ZOLTAN_ID_TYPE *, ZOLTAN_ID_TYPE *,
        int *, ZOLTAN_ID_TYPE **, int **, ZOLTAN_ID_TYPE **);
  
  static int process_mtxp_file(PARIO_INFO_PTR, char *, size_t , int , int , int, 
      ZOLTAN_ID_TYPE *, ZOLTAN_ID_TYPE *, int *, int *, int *, 
      ZOLTAN_ID_TYPE **, ZOLTAN_ID_TYPE **, int *, ZOLTAN_ID_TYPE **, 
      float **, int *, ZOLTAN_ID_TYPE **, float **, int);
  
  static int read_mtxp_lines(char *, int, int, char **, int *);
  static char *get_nth_token(char *, int , size_t , int , char );
  static char *first_char(char *, size_t);
  static void make_string(char *, char *);
  static void section_starts(char *, char **, char **, char **);
  static char *next_line(char *, size_t);
  static char *next_line_of_data(char *, size_t, char *);
  static char *next_line_of_comment(char *, size_t, char *);
  
  static int my_vtx(int , ZOLTAN_ID_TYPE , ZOLTAN_ID_TYPE , ZOLTAN_ID_TYPE ,
         int , int , PARIO_INFO_PTR );
  static int my_pin(ZOLTAN_ID_TYPE , ZOLTAN_ID_TYPE , int ,
         ZOLTAN_ID_TYPE , ZOLTAN_ID_TYPE , ZOLTAN_ID_TYPE , ZOLTAN_ID_TYPE ,
         int , int , PARIO_INFO_PTR );
  
  static int _zoltan_sortFunc(const void *, const void *);
  
  /****************************************************************************/
  /****************************************************************************/
  /****************************************************************************/
  
  /* Read in MatrixMarket file.
   * For now, just call the hypergraph routine.
   * In the future we may want to allow both graphs and hypergraphs
   * to be read in MatrixMarket format.
   *
   */
  int read_mm_file(
    int Proc,
    int Num_Proc,
    PROB_INFO_PTR prob,
    PARIO_INFO_PTR pio_info,
    MESH_INFO_PTR mesh
  )
  {
    return read_hypergraph_file(Proc, Num_Proc, prob, pio_info, mesh);
  }
  
  /* Read from file and set up hypergraph. */
  int read_hypergraph_file(
    int Proc,
    int Num_Proc,
    PROB_INFO_PTR prob,
    PARIO_INFO_PTR pio_info,
    MESH_INFO_PTR mesh
  )
  {
    /* Local declarations. */
    const char  *yo = "read_hypergraph_file";
    char   cmesg[FILENAME_MAX+256];
  
    int    i, distributed_pins = 0, vertex, nextEdge;
    int    nvtxs = 0, nhedges = 0, npins = 0;
    int    vwgt_dim=0, hewgt_dim=0, vtx, edgeSize;
    int   *hindex = NULL, *hvertex_proc = NULL;
    int   *hgid = NULL, *hvertex = NULL;
    int edge, global_npins, gnhedges;
    int   gnvtxs;
    float *hewgts = NULL, *vwgts = NULL;
    ZOLTAN_FILE* fp = NULL;
    int base = 0;   /* Smallest vertex number; usually zero or one. */
    char filename[FILENAME_MAX+9];
  
    /* Variables that allow graph-based functions to be reused. */
    /* If no chaco.graph or chaco.coords files exist, values are NULL or 0,
     * since graph is not being built. If chaco.graph and/or chaco.coords
     * exist, these arrays are filled and values stored in mesh.
     * Including these files allows for comparison of HG methods with other
     * methods, along with visualization of results and comparison of
     * LB_Eval results.
     */
    int    ch_nvtxs = 0;        /* Temporary values for chaco_read_graph.   */
  #ifdef KDDKDD
    int    ch_vwgt_dim = 0;     /* Their values are ignored, as vertex      */
  #endif
    float *ch_vwgts = NULL;     /* info is provided by hypergraph file.     */
    int   *ch_start = NULL, *ch_adj = NULL, ch_ewgt_dim = 0;
    short *ch_assignments = NULL;
    float *ch_ewgts = NULL;
    int    ch_ndim = 0;
    float *ch_x = NULL, *ch_y = NULL, *ch_z = NULL;
    int    ch_no_geom = TRUE;   /* Assume no geometry info is given; reset if
  				 it is provided. */
    int    file_error = 0;
  
  /***************************** BEGIN EXECUTION ******************************/
  
    DEBUG_TRACE_START(Proc, yo);
  
    if (Proc == 0) {
  
      /* Open and read the hypergraph file. */
      if (pio_info->file_type == HYPERGRAPH_FILE)
        sprintf(filename, "%s.hg", pio_info->pexo_fname);
      else if (pio_info->file_type == MATRIXMARKET_FILE)
        sprintf(filename, "%s.mtx", pio_info->pexo_fname);
      else {
  	sprintf(cmesg, "fatal:  invalid file type %d", pio_info->file_type);
  	Gen_Error(0, cmesg);
  	return 0;
      }
  
      fp = ZOLTAN_FILE_open(filename, "r", pio_info->file_comp);
      file_error = (fp == NULL);
    }
  
  
  
    MPI_Bcast(&file_error, 1, MPI_INT, 0, MPI_COMM_WORLD);
  
    if (file_error) {
      sprintf(cmesg,
        "fatal:  Could not open hypergraph file %s",pio_info->pexo_fname);
      Gen_Error(0, cmesg);
      return 0;
    }
  
    if (pio_info->file_type == HYPERGRAPH_FILE) {
      /* read the array in on processor 0 */
      if (Proc == 0) {
        if (HG_readfile(Proc, fp, &nvtxs, &nhedges, &npins,
  		    &hindex, &hvertex, &vwgt_dim, &vwgts,
  		    &hewgt_dim, &hewgts, &base) != 0){
  	Gen_Error(0, "fatal: Error returned from HG_readfile");
  	return 0;
        }
      }
    }
    else if (pio_info->file_type == MATRIXMARKET_FILE) {
      /*
       * pio_info->chunk_reader == 0  (the usual case)
       *   process 0 will read entire file in MM_readfile,
       *   and will distribute vertices in chaco_dist_graph and pins in
       *   dist_hyperedges later.   (distributed_pins==0)
       *
       * pio_info->chunk_reader == 1  ("initial read = chunks" in zdrive.inp)
       *   process 0 will read the file in chunks, and will send vertices
       *   and pins to other processes before reading the next chunk, all
       *   in MM_readfile.  (distributed_pins==1)
       */
  
      if (MM_readfile(Proc, Num_Proc, fp, pio_info,
  		    &nvtxs,     /* global number of vertices */
  		    &nhedges,   /* global number of hyperedges */
  		    &npins,     /* local number of pins */
  		    &hindex, &hvertex, &vwgt_dim, &vwgts,
  		    &hewgt_dim, &hewgts, &ch_start, &ch_adj,
  		    &ch_ewgt_dim, &ch_ewgts, &base, &global_npins)) {
        Gen_Error(0, "fatal: Error returned from MM_readfile");
        return 0;
      }
  
      if (Proc == 0) ZOLTAN_FILE_close(fp);
  
      if ((Num_Proc > 1) && pio_info->chunk_reader && (global_npins > Num_Proc)){
        distributed_pins = 1;
      }
      else{
        distributed_pins = 0;
      }
    }
  
  
  #ifdef KDDKDD
   {
     /* If CHACO graph file is available, read it. */
  
     sprintf(filename, "%s.graph", pio_info->pexo_fname);
  
     fp = ZOLTAN_FILE_open(filename, "r", pio_info->file_comp);
     file_error =
  #ifndef ZOLTAN_COMPRESS
       (fp == NULL);
  #else
     fp.error;
  #endif
  
  
     if (!file_error) {
        /* CHACO graph file is available. */
        /* Assuming hypergraph vertices are same as chaco vertices. */
        /* Chaco vertices and their weights are ignored in rest of function. */
        if (chaco_input_graph(fp, filename, &ch_start, &ch_adj, &ch_nvtxs,
  		      &ch_vwgt_dim, &ch_vwgts, &ch_ewgt_dim, &ch_ewgts) != 0) {
  	Gen_Error(0, "fatal: Error returned from chaco_input_graph");
  	return 0;
        }
      }
     else
       ch_nvtxs = nvtxs;
  
  
      /* If coordinate file is available, read it. */
     sprintf(filename, "%s.coords", pio_info->pexo_fname);
  
     fp = ZOLTAN_FILE_open(filename, "r", pio_info->file_comp);
     file_error =
  #ifndef ZOLTAN_COMPRESS
       (fp == NULL);
  #else
     fp.error;
  #endif
  
      if (!file_error) {
        /* CHACO coordinates file is available. */
        ch_no_geom = FALSE;
        if (chaco_input_geom(fpkdd, filename, ch_nvtxs, &ch_ndim,
  			   &ch_x, &ch_y, &ch_z) != 0) {
  	Gen_Error(0, "fatal: Error returned from chaco_input_geom");
  	return 0;
        }
      }
   }
  #else /* KDDKDD */
    ch_nvtxs = nvtxs;
  #endif /* KDDKDD */
  
  
    {
       /* Read Chaco assignment file, if requested */
     if (pio_info->init_dist_type == INITIAL_FILE) {
       sprintf(filename, "%s.assign", pio_info->pexo_fname);
  
     fp = ZOLTAN_FILE_open(filename, "r", pio_info->file_comp);
  
     if (fp == NULL) {
       sprintf(cmesg, "Error:  Could not open Chaco assignment file %s; "
  	     "initial distribution cannot be read",
  	     filename);
       Gen_Error(0, cmesg);
       return 0;
     }
     else {
       /* read the coordinates in on processor 0 */
       ch_assignments = (short *) malloc(nvtxs * sizeof(short));
       if (nvtxs && !ch_assignments) {
         Gen_Error(0, "fatal: memory error in read_hypergraph_file");
         return 0;
       }
       /* closes fpassign when done */
       if (chaco_input_assign(fp, filename, ch_nvtxs, ch_assignments) != 0){
         Gen_Error(0, "fatal: Error returned from chaco_input_assign");
         return 0;
       }
     }
     }
   }
  
    MPI_Bcast(&base, 1, MPI_INT, 0, MPI_COMM_WORLD);
  
    if (distributed_pins){
      gnhedges = nhedges;
      nhedges = 0;
      hewgt_dim = 0;
      hewgts = NULL;
      for (edge=0; edge<gnhedges; edge++){
        edgeSize = hindex[edge+1] - hindex[edge];
        if (edgeSize > 0) nhedges++;
      }
      hgid = (int *)malloc(nhedges * sizeof(int));
      hvertex_proc = (int *)malloc(npins * sizeof(int));
      nextEdge=0;
      vtx=0;
      for (edge=0; edge<gnhedges; edge++){
        edgeSize = hindex[edge+1] - hindex[edge];
        if (edgeSize > 0){
  	hgid[nextEdge] = edge+1;
  	if (nextEdge < edge){
  	  hindex[nextEdge+1] = hindex[nextEdge] + edgeSize;
  	}
  	for (vertex=0; vertex<edgeSize; vertex++,vtx++){
  	  hvertex_proc[vtx] = ch_dist_proc(hvertex[vtx], NULL, 1);
  	}
  	nextEdge++;
        }
      }
      gnvtxs = nvtxs;
      nvtxs = ch_dist_num_vtx(Proc, NULL);
      if (ch_start){    /* need to include only vertices this process owns */
        for (i=0,vertex=0; i<gnvtxs; i++){
  	if ((ch_start[i+1] > ch_start[vertex]) || /* vtx has adjacencies so it's mine */
  	    (ch_dist_proc(i, NULL, 0) == Proc))   /* my vtx with no adjacencies */
  	  {
  	  if (i > vertex){
  	    ch_start[vertex+1] = ch_start[i+1];
  	  }
  	  vertex++;
  	}
        }
      }
  #if PRINT_DEBUG_INFO
      debug_lists(Proc, Num_Proc, nhedges, hindex, hvertex, hvertex_proc, hgid);
  #endif
    } else{
  
      /* Distribute hypergraph graph */
      /* Use hypergraph vertex information and chaco edge information. */
  
      if (!chaco_dist_graph(MPI_COMM_WORLD, pio_info, 0, &gnvtxs, &nvtxs,
  	     &ch_start, &ch_adj, &vwgt_dim, &vwgts, &ch_ewgt_dim, &ch_ewgts,
  	     &ch_ndim, &ch_x, &ch_y, &ch_z, &ch_assignments)) {
        Gen_Error(0, "fatal: Error returned from chaco_dist_graph");
        return 0;
      }
  
      if (!dist_hyperedges(MPI_COMM_WORLD, pio_info, 0, base, gnvtxs, &gnhedges,
  		       &nhedges, &hgid, &hindex, &hvertex, &hvertex_proc,
  		       &hewgt_dim, &hewgts, ch_assignments)) {
        Gen_Error(0, "fatal: Error returned from dist_hyperedges");
        return 0;
      }
    }
  
    /* Initialize mesh structure for Hypergraph. */
    mesh->data_type = ZOLTAN_HYPERGRAPH;
    mesh->num_elems = nvtxs;
    mesh->vwgt_dim = vwgt_dim;
    mesh->ewgt_dim = ch_ewgt_dim;
    mesh->elem_array_len = mesh->num_elems + 5;
    mesh->num_dims = ch_ndim;
    mesh->num_el_blks = 1;
  
    mesh->gnhedges = (ZOLTAN_ID_TYPE)gnhedges;
    mesh->nhedges = nhedges;
    mesh->hewgt_dim = hewgt_dim;
  
    mesh->hindex = hindex;
    mesh->hvertex_proc = hvertex_proc;
    mesh->heNumWgts = nhedges;
    mesh->hewgts = hewgts;
  
    mesh->hgid = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * nhedges);
    mesh->hvertex = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * hindex[nhedges]);
  
    for (i=0; i < nhedges; i++){
      mesh->hgid[i] = (ZOLTAN_ID_TYPE)hgid[i];
    }
    free(hgid);
  
    for (i=0; i < hindex[nhedges]; i++){
      mesh->hvertex[i] = (ZOLTAN_ID_TYPE)hvertex[i];
    }
    free(hvertex);
  
    mesh->heWgtId = NULL;
  
    mesh->eb_etypes = (int *) malloc (4 * mesh->num_el_blks * sizeof(int));
    if (!mesh->eb_etypes) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
    mesh->eb_ids = mesh->eb_etypes + mesh->num_el_blks;
    mesh->eb_nnodes = mesh->eb_ids + mesh->num_el_blks;
    mesh->eb_nattrs = mesh->eb_nnodes + mesh->num_el_blks;
  
    mesh->eb_cnts = (ZOLTAN_ID_TYPE *) malloc ( mesh->num_el_blks * sizeof(ZOLTAN_ID_TYPE));
  
    mesh->eb_names = (char **) malloc (mesh->num_el_blks * sizeof(char *));
    if (!mesh->eb_names) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
  
    mesh->eb_etypes[0] = -1;
    mesh->eb_ids[0] = 1;
    mesh->eb_cnts[0] = (ZOLTAN_ID_TYPE)nvtxs;
    mesh->eb_nattrs[0] = 0;
    /*
     * Each element has one set of coordinates (i.e., node) if a coords file
     * was provided; zero otherwise.
     */
    MPI_Bcast( &ch_no_geom, 1, MPI_INT, 0, MPI_COMM_WORLD);
    if (ch_no_geom)
      mesh->eb_nnodes[0] = 0;
    else
      mesh->eb_nnodes[0] = 1;
  
    /* allocate space for name */
    mesh->eb_names[0] = (char *) malloc((MAX_STR_LENGTH+1) * sizeof(char));
    if (!mesh->eb_names[0]) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
    strcpy(mesh->eb_names[0], "hypergraph");
  
    /* allocate the element structure array */
    mesh->elements = (ELEM_INFO_PTR) malloc (mesh->elem_array_len * sizeof(ELEM_INFO));
    if (!(mesh->elements)) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
  
    /*
     * initialize all of the element structs as unused by
     * setting the globalID to ZOLTAN_ID_INVALID 
     */
    for (i = 0; i < mesh->elem_array_len; i++)
      initialize_element(&(mesh->elements[i]));
  
    /*
     * now fill the element structure array with the
     * information from the Chaco file
     * Use hypergraph vertex information and chaco edge information.
     */
  
    if (!chaco_fill_elements(Proc, Num_Proc, prob, mesh, pio_info, (ZOLTAN_ID_TYPE)gnvtxs, nvtxs,
  		     ch_start, ch_adj, vwgt_dim, vwgts, ch_ewgt_dim, ch_ewgts,
  		     ch_ndim, ch_x, ch_y, ch_z, ch_assignments, base)) {
      Gen_Error(0, "fatal: Error returned from chaco_fill_elements");
      return 0;
    }
  #if PRINT_DEBUG_INFO
    debug_elements(Proc, Num_Proc, mesh->num_elems,mesh->elements);
  #endif
  
    safe_free((void **)(void *) &vwgts);
    safe_free((void **)(void *) &ch_ewgts);
    safe_free((void **)(void *) &ch_vwgts);
    safe_free((void **)(void *) &ch_x);
    safe_free((void **)(void *) &ch_y);
    safe_free((void **)(void *) &ch_z);
    safe_free((void **)(void *) &ch_start);
    safe_free((void **)(void *) &ch_adj);
    safe_free((void **)(void *) &ch_assignments);
  
   if (Debug_Driver > 3)
     print_distributed_mesh(Proc, Num_Proc, mesh);
  
    DEBUG_TRACE_END(Proc, yo);
    return 1;
  }
  
  /* Read from mtxp file (such as that written out by Zoltan_Generate_Files)
   * and set up the hypergraph. */
  
  int read_mtxplus_file(
    int Proc,
    int Num_Proc,
    PROB_INFO_PTR prob,
    PARIO_INFO_PTR pio_info,
    MESH_INFO_PTR mesh
  )
  {
    /* Local declarations. */
    const char  *yo = "read_mtxplus_file";
    char filename[FILENAME_MAX+9], cmesg[FILENAME_MAX+256];
    struct stat statbuf;
    int rc, fsize, i, j;
    char *filebuf=NULL;
    FILE* fp;
    ZOLTAN_ID_TYPE nGlobalEdges, nGlobalVtxs; 
    ZOLTAN_ID_TYPE *myPinI, *myPinJ, *myVtxNum, *myEWGno;
    ZOLTAN_ID_TYPE *edgeGno, *pinGno;
    int vtxWDim, edgeWDim;
    int nMyPins, nMyVtx, nMyEdgeWgts;
    float *myVtxWgts, *myEdgeWgts;
    int status;
    int numHEdges;
    int *edgeIdx;
    int num_my_lines, preprocessed;
  
    DEBUG_TRACE_START(Proc, yo);
  
    if (pio_info->init_dist_procs < 0){
      pio_info->init_dist_procs = Num_Proc;
    }
  
    num_my_lines = -1;
    preprocessed = 0;
    sprintf(filename, "%s.mtxp", pio_info->pexo_fname);
    if (pio_info->file_comp == GZIP)
      sprintf(filename, "%s.gz", filename);      /* but we don't uncompress?? TODO */
  
    if (pio_info->chunk_reader == 1 &&              /* read large file in chunks */
        pio_info->init_dist_type == INITIAL_OWNER)  /* each process gets its own objects */
    {
      /* Each process reads in the mtxp file and keeps only the parts that it owns.
       * Buffer is null terminated.
       * It's possible a process only has mtxp header and owns no vertices, etc.
       */                                     
  
      fsize = read_mtxp_lines(filename, Proc, Num_Proc, &filebuf, &num_my_lines);
  
      if (fsize == 0){
        Gen_Error(0, "fatal: insufficient memory or invalid mtxp file");  /* TODO better message */
        return 0;
      }
  
      preprocessed = 1;
    }
    else if (pio_info->chunk_reader == 0)
    {
      /* Process 0 reads the file and broadcasts it */
      if (Proc == 0) {
        fsize = 0;
    
        rc = stat(filename, &statbuf);
    
        if (rc == 0){
          fsize = statbuf.st_size;
          fp = fopen(filename, "r");
    
          if (!fp){
    	  fsize = 0;
          }
          else{
    	filebuf = (char *)malloc(fsize+1);
    
    	rc = fread(filebuf, 1, fsize, fp);
    
    	if (rc != fsize){
    	  free(filebuf);
    	  fsize = 0;
    	  fp = NULL;
    	}
    	else{
    	  filebuf[fsize] = 0;
    	  fsize++;
    	}
    	fclose(fp);
          }
        }
      }
    
      MPI_Bcast(&fsize, 1, MPI_INT, 0, MPI_COMM_WORLD);
    
      if (fsize == 0) {
        sprintf(cmesg, "fatal:  Could not open/read hypergraph file %s", filename);
        Gen_Error(0, cmesg);
        return 0;
      }
    
      if (Proc > 0){
        filebuf = (char *)malloc(fsize);
      }
    
      MPI_Bcast(filebuf, fsize, MPI_BYTE, 0, MPI_COMM_WORLD);
    }
    else{
      /* ERROR - we don't handle the zdrive.inp file request */
      Gen_Error(0, "fatal: read_mtxplus_file can not handle this request");
      return 0;
    }
  
    rc = process_mtxp_file(pio_info, filebuf, fsize, num_my_lines, Num_Proc, Proc,
  	  &nGlobalEdges, &nGlobalVtxs, &vtxWDim, &edgeWDim,
            &nMyPins, &myPinI, &myPinJ,
    	  &nMyVtx, &myVtxNum, &myVtxWgts,
    	  &nMyEdgeWgts, &myEWGno, &myEdgeWgts, preprocessed);
  
    free(filebuf);
    
    MPI_Allreduce(&rc, &status, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
    
    if (status != Num_Proc){
      Gen_Error(0, "fatal: invalid mtxp file");  /* TODO better message */
      return 0;
    }
  
  #if PRINT_DEBUG_INFO
    debug_pins(Proc, Num_Proc, nGlobalEdges, nGlobalVtxs, vtxWDim, edgeWDim,
            nMyPins, myPinI, myPinJ,
    	  nMyVtx, myVtxNum, myVtxWgts,
    	  nMyEdgeWgts, myEWGno, myEdgeWgts);
  
  #endif
  
    /*
     * From the lists of pins, create edge lists.  (Unless
     * the initial pin distribution is by column, in which
     * case we will test the hypergraph query interface's
     * ability to accept pins by column rather than row.)
     *
     * initial pins = row     each process gets full rows (hedges) initially
     *                        and format is ZOLTAN_COMPRESSED_EDGE
     *
     * initial pins = col     each process gets full columns (vertices) initially 
     *                        and format is ZOLTAN_COMPRESSED_VERTEX
     */
  
    if (pio_info->init_dist_pins != INITIAL_COL){  
      rc = create_edge_lists(nMyPins, myPinI, myPinJ, &numHEdges, &edgeGno, &edgeIdx, &pinGno);
      mesh->format = ZOLTAN_COMPRESSED_EDGE;
    }
    else{                                       
      /* actually creating vertex lists, since we switched
       * the role of I and J in the argument list.
       */
      rc = create_edge_lists(nMyPins, myPinJ, myPinI,
  	    &numHEdges, &edgeGno, &edgeIdx, &pinGno);
      mesh->format = ZOLTAN_COMPRESSED_VERTEX;
    }
  
    MPI_Allreduce(&rc, &status, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
  
    if (status != Num_Proc){
      return 0;
    }
  
    safe_free((void **)(void *)&myPinI);
    safe_free((void **)(void *)&myPinJ);
  
    /* Initialize mesh structure for Hypergraph. */
    mesh->data_type = ZOLTAN_HYPERGRAPH;
    mesh->num_elems = nMyVtx;
    mesh->vwgt_dim = vtxWDim;
    mesh->ewgt_dim = 0;
    mesh->elem_array_len = mesh->num_elems + 5;
    mesh->num_dims = 0;
    mesh->num_el_blks = 1;
  
    mesh->gnhedges = (ZOLTAN_ID_TYPE)nGlobalEdges;
    mesh->nhedges = numHEdges;     /* (or num vertices if CCS) */
    mesh->hewgt_dim = edgeWDim;
  
    mesh->hvertex_proc = NULL;     /* don't know don't care */
    mesh->hindex = edgeIdx;        /* (or vertex index if CCS) */
    mesh->heNumWgts = nMyEdgeWgts;
    mesh->hewgts = myEdgeWgts;
  
    if (numHEdges){
      mesh->hgid = (ZOLTAN_ID_TYPE *)malloc(numHEdges * sizeof(ZOLTAN_ID_TYPE));
      for (i=0; i < numHEdges; i++){
        mesh->hgid[i] = (ZOLTAN_ID_TYPE)edgeGno[i];
      }
      free(edgeGno);
  
      if (edgeIdx[numHEdges]){
        mesh->hvertex = (ZOLTAN_ID_TYPE *)malloc(edgeIdx[numHEdges] * sizeof(ZOLTAN_ID_TYPE));
        for (i=0; i < edgeIdx[numHEdges]; i++){
          mesh->hvertex[i] = (ZOLTAN_ID_TYPE)pinGno[i];
        }
        free(pinGno);
      }
    }
  
    if (nMyEdgeWgts){
      mesh->heWgtId = (ZOLTAN_ID_TYPE *)malloc(nMyEdgeWgts * sizeof(ZOLTAN_ID_TYPE));
      for (i=0; i < nMyEdgeWgts; i++){
        mesh->heWgtId[i] = (ZOLTAN_ID_TYPE)myEWGno[i];
      }
      free(myEWGno);
    }
  
    mesh->eb_etypes = (int *) malloc (4 * mesh->num_el_blks * sizeof(int));
    if (!mesh->eb_etypes) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
    mesh->eb_ids = mesh->eb_etypes + mesh->num_el_blks;
    mesh->eb_nnodes = mesh->eb_ids + mesh->num_el_blks;
    mesh->eb_nattrs = mesh->eb_nnodes + mesh->num_el_blks;
  
    mesh->eb_cnts = (ZOLTAN_ID_TYPE *) malloc (mesh->num_el_blks * sizeof(ZOLTAN_ID_TYPE));
    if (!mesh->eb_cnts) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
  
    mesh->eb_names = (char **) malloc (mesh->num_el_blks * sizeof(char *));
    if (!mesh->eb_names) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
  
    mesh->eb_etypes[0] = -1;
    mesh->eb_ids[0] = 1;
    mesh->eb_cnts[0] = nGlobalVtxs;
    mesh->eb_nattrs[0] = 0;
    mesh->eb_nnodes[0] = 0;
  
    /* allocate space for name */
    mesh->eb_names[0] = (char *) malloc((MAX_STR_LENGTH+1) * sizeof(char));
    if (!mesh->eb_names[0]) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
    strcpy(mesh->eb_names[0], "hypergraph");
  
    /* allocate the element structure array */
    mesh->elements = (ELEM_INFO_PTR) malloc (mesh->elem_array_len
  					 * sizeof(ELEM_INFO));
    if (!(mesh->elements)) {
      Gen_Error(0, "fatal: insufficient memory");
      return 0;
    }
  
    /*
     * Write the element structure with the vertices and weights
     */
    for (i = 0; i < mesh->elem_array_len; i++) {
      initialize_element(&(mesh->elements[i]));
      if (i < mesh->num_elems){
        mesh->elements[i].globalID = (ZOLTAN_ID_TYPE)myVtxNum[i];
        mesh->elements[i].my_part  = Proc;
        for (j=0; j<vtxWDim; j++){
  	mesh->elements[i].cpu_wgt[j] = myVtxWgts[i*vtxWDim + j];
        }
      }
    }
  
    safe_free((void **)(void *) &myVtxWgts);
    safe_free((void **)(void *) &myVtxNum);
  
   if (Debug_Driver > 3)
     print_distributed_mesh(Proc, Num_Proc, mesh);
  
    DEBUG_TRACE_END(Proc, yo);
    return 1;
  }
  
  /****************************************************************************/
  /****************************************************************************/
  /****************************************************************************/
  
  void mm_cleanup(MESH_INFO_PTR mesh)
  {
  /* Matrix-market files are one-based, but when we read them, we converted
   * them to zero-based for convenience of indexing arrays.
   * However, we should give Zoltan and the output routines IDs 
   * that have the same base as the input (not converted from one-based 
   * to zero-based).  So if the input was from Matrix-Market files, 
   * add one to all GIDs.
   */
    
    int i, j, sum;
    int nhedges = mesh->nhedges;
    int npins = mesh->hindex[nhedges];
  
    ELEM_INFO_PTR elements = mesh->elements;
   
    for (i = 0; i < mesh->num_elems; i++) {
      ELEM_INFO_PTR current_elem = &(elements[i]);
      current_elem->globalID++;
      for (j = 0; j < current_elem->adj_len; j++) {
        if (current_elem->adj[j] == ZOLTAN_ID_INVALID) continue;
        if (current_elem->adj_proc[j] != mesh->proc) {
          current_elem->adj[j]++;
        }
      }
    }
    for (i = 0; i < nhedges; i++) mesh->hgid[i]++;
    for (i = 0; i < npins; i++) mesh->hvertex[i]++;
    sum = 0;
    for (i = 0; i < mesh->necmap; i++) sum += mesh->ecmap_cnt[i];
    for (i = 0; i < sum; i++) mesh->ecmap_neighids[i]++;
  }
  
  /*****************************************************************************/
  static int dist_hyperedges(
    MPI_Comm comm,		/* MPI Communicator */
    PARIO_INFO_PTR pio_info,      /* Parallel IO info */
    int     host_proc,		/* processor where all the data is initially */
    int     base,                 /* indicates whether input is 0-based or
  				   1-based (i.e., is lowest vertex number
  				   0 or 1?). */
    int     gnvtxs,               /* global number of vertices */
    int     *gnhedges,		/* global number of hyperedges */
    int     *nhedges,		/* local number of hyperedges */
    int     **hgid,		/* global hyperedge numbers */
    int     **hindex,		/* Starting hvertex entry for hyperedges */
    int     **hvertex,		/* Array of vertices in hyperedges; returned
  				   values are global IDs for vertices */
    int     **hvertex_proc,	/* Array of processor assignments for
  				   vertices in hvertex.  */
    int     *hewgt_dim,           /* number of weights per hyperedge */
    float   **hewgts,		/* hyperedge weight list data */
    short   *assignments          /* assignments from Chaco file; may be NULL */
  )
  {
  /*
   * Distribute hyperedges from one processor to all processors.
   * Vertex distribution is assumed already done through chaco_dist_graph.
   * The memory for the hyperedges on the host node is freed
   * and fresh memory is allocated for the distributed hyperedges.
   */
  
  const char *yo = "dist_hyperedges";
  int nprocs, myproc, i, h, p = 0;
  int *old_hindex = NULL, *old_hvertex = NULL, *old_hvertex_proc = NULL;
  int *size = NULL, *num_send = NULL;
  int *send = NULL;
  int *send_hgid = NULL, *send_hindex = NULL;
  int *send_hvertex = NULL, *send_hvertex_proc = NULL;
  int max_size, max_num_send;
  int hcnt[2];
  int hecnt, hvcnt;
  float *old_hewgts = NULL;
  float *send_hewgts = NULL;
  MPI_Status status;
  int num_dist_procs;
  int hedge_init_dist_type;
  
    hedge_init_dist_type = (pio_info->init_dist_type != INITIAL_FILE
  			  ? pio_info->init_dist_type
  			  : INITIAL_LINEAR);
  
    /* Determine number of processors and my rank. */
    MPI_Comm_size (comm, &nprocs );
    MPI_Comm_rank (comm, &myproc );
  
    DEBUG_TRACE_START(myproc, yo);
  
    if (pio_info->init_dist_procs > 0 && pio_info->init_dist_procs <= nprocs)
      num_dist_procs = pio_info->init_dist_procs;
    else
      /* Reset num_dist_procs if not set validly by input */
      num_dist_procs = nprocs;
  
    /* Broadcast to all procs */
    MPI_Bcast( hewgt_dim, 1, MPI_INT, host_proc, comm);
    MPI_Bcast( nhedges, 1, MPI_INT, host_proc, comm);
    *gnhedges = *nhedges;
  
    /* Initialize */
    if (*gnhedges == 0) {
      *hindex = (int *) malloc(sizeof(int));
      if (!(*hindex)) {
        Gen_Error(0, "fatal: insufficient memory");
        return 0;
      }
      (*hindex)[0] = 0;
      *hvertex = NULL;
      *hvertex_proc = NULL;
      return 1;
    }
  
    if (nprocs == 1) {
      *nhedges = *gnhedges;
      *hgid = (int *) malloc(*gnhedges * sizeof(int));
      *hvertex_proc = (int *) malloc((*hindex)[*gnhedges] * sizeof(int));
      if ((*gnhedges && !(*hgid)) || ((*hindex)[*gnhedges] && !(*hvertex_proc))) {
        Gen_Error(0, "fatal: insufficient memory");
        return 0;
      }
      for (h = 0; h < *gnhedges; h++)
        (*hgid)[h] = h + base;           /* Want the same numbering than for vertices */
      for (h = 0; h < (*hindex)[*gnhedges]; h++)
        (*hvertex_proc)[h] = 0;
      return 1;
    }
    if (myproc == host_proc) {
      /* Store pointers to original data */
      old_hindex  = *hindex;
      old_hvertex = *hvertex;
      old_hewgts = *hewgts;
      old_hvertex_proc = (int *) malloc(old_hindex[*gnhedges] * sizeof(int));
  
      /* Allocate space for size and send flags */
      size = (int *) calloc(2 * nprocs, sizeof(int));
      num_send = size + nprocs;
      send = (int *) malloc(*gnhedges * sizeof(int *));
      if ((old_hindex[*gnhedges] && !old_hvertex_proc) || !size ||
          (*gnhedges && !send)) {
        Gen_Error(0, "fatal: memory error");
        return 0;
      }
  
      /* Determine to which processors hyperedges should be sent */
      for (h = 0; h < *gnhedges; h++) {
        if (hedge_init_dist_type == INITIAL_CYCLIC)
  	p = h % num_dist_procs;
        else if (hedge_init_dist_type == INITIAL_LINEAR)
  	p = (int) ((float) h * (float) num_dist_procs / (float)(*gnhedges));
        else if (hedge_init_dist_type == INITIAL_OWNER)
  	p = ch_dist_proc(old_hvertex[old_hindex[h]], assignments, base);
        size[p] += (old_hindex[h+1] - old_hindex[h]);
        num_send[p]++;
        send[h] = p;
        for (i = old_hindex[h]; i < old_hindex[h+1]; i++)
  	old_hvertex_proc[i] = ch_dist_proc(old_hvertex[i], assignments, base);
      }
  
      /* Determine size of send buffers and allocate them. */
      max_size = 0;
      max_num_send = 0;
      for (p = 0; p < nprocs; p++) {
        if (size[p] > max_size) max_size = size[p];
        if (num_send[p] > max_num_send) max_num_send = num_send[p];
      }
  
      send_hgid = (int *) malloc((max_num_send) * sizeof(int));
      send_hindex = (int *) malloc((max_num_send+1) * sizeof(int));
      send_hvertex = (int *) malloc(max_size * sizeof(int));
      send_hvertex_proc = (int *) malloc(max_size * sizeof(int));
      if (*hewgt_dim)
        send_hewgts = (float *) malloc(max_num_send*(*hewgt_dim)*sizeof(float));
      if ((max_num_send && !send_hgid) || !send_hindex ||
  	(max_size && (!send_hvertex || !send_hvertex_proc)) ||
  	(max_num_send && *hewgt_dim && !send_hewgts)) {
        Gen_Error(0, "fatal: memory error in dist_hyperedges");
        return 0;
      }
  
      /* Load and send data */
      for (p = 0; p < nprocs; p++) {
  
        if (p == myproc) continue;
  
        hecnt = 0;
        hvcnt = 0;
        send_hindex[0] = 0;
        for (h = 0; h < *gnhedges; h++) {
  	if (send[h]==p) {
  	  send_hgid[hecnt] = h + base; /* Want the same numbering than for vertices */
  	  send_hindex[hecnt+1] = send_hindex[hecnt]
  			       + (old_hindex[h+1] - old_hindex[h]);
  	  for (i = 0; i < *hewgt_dim; i++)
  	    send_hewgts[hecnt*(*hewgt_dim)+i] = old_hewgts[h*(*hewgt_dim)+i];
  	  hecnt++;
  	  for (i = old_hindex[h]; i < old_hindex[h+1]; i++) {
  	    send_hvertex[hvcnt] = old_hvertex[i];
  	    send_hvertex_proc[hvcnt] = old_hvertex_proc[i];
  	    hvcnt++;
  	  }
  	}
        }
  
        hcnt[0] = hecnt;
        hcnt[1] = hvcnt;
  
        MPI_Send(hcnt, 2, MPI_INT, p, 1, comm);
        MPI_Send(send_hgid, hecnt, MPI_INT, p, 2, comm);
        MPI_Send(send_hindex, hecnt+1, MPI_INT, p, 3, comm);
        MPI_Send(send_hvertex, hvcnt, MPI_INT, p, 4, comm);
        MPI_Send(send_hvertex_proc, hvcnt, MPI_INT, p, 5, comm);
        if (*hewgt_dim)
  	MPI_Send(send_hewgts, hecnt*(*hewgt_dim), MPI_FLOAT, p, 6, comm);
      }
  
      safe_free((void **)(void *) &send_hgid);
      safe_free((void **)(void *) &send_hindex);
      safe_free((void **)(void *) &send_hvertex);
      safe_free((void **)(void *) &send_hvertex_proc);
      safe_free((void **)(void *) &send_hewgts);
  
      /* Copy data owned by myproc into new local storage */
      *nhedges = num_send[myproc];
      *hgid = (int *) malloc(*nhedges * sizeof(int));
      *hindex = (int *) malloc((*nhedges+1) * sizeof(int));
      *hvertex = (int *) malloc(size[myproc] * sizeof(int));
      *hvertex_proc = (int *) malloc(size[myproc] * sizeof(int));
      if (*hewgt_dim)
        *hewgts = (float *) malloc(*nhedges * *hewgt_dim * sizeof(float));
      if ((*nhedges && !(*hgid)) || !(*hindex) ||
  	(size[myproc] && (!(*hvertex) || !(*hvertex_proc))) ||
  	(*nhedges && *hewgt_dim && !(*hewgts))) {
        Gen_Error(0, "fatal: memory error in dist_hyperedges");
        return 0;
      }
  
      hecnt = 0;
      hvcnt = 0;
      (*hindex)[0] = 0;
  
      for (h = 0; h < *gnhedges; h++) {
        if (send[h]==myproc) {
  	(*hgid)[hecnt] = h + base;  /* Want the same numbering than for vertices */
  	(*hindex)[hecnt+1] = (*hindex)[hecnt]
  			   + (old_hindex[h+1] - old_hindex[h]);
  	for (i = 0; i < *hewgt_dim; i++)
  	  (*hewgts)[hecnt*(*hewgt_dim)+i] = old_hewgts[h*(*hewgt_dim)+i];
  	hecnt++;
  	for (i = old_hindex[h]; i < old_hindex[h+1]; i++) {
  	  (*hvertex_proc)[hvcnt] = old_hvertex_proc[i];
  	  (*hvertex)[hvcnt] = old_hvertex[i];  /* Global index */
  	  hvcnt++;
  	}
        }
      }
  
      safe_free((void **)(void *) &send);
      safe_free((void **)(void *) &size);
      safe_free((void **)(void *) &old_hindex);
      safe_free((void **)(void *) &old_hvertex);
      safe_free((void **)(void *) &old_hvertex_proc);
      safe_free((void **)(void *) &old_hewgts);
    }
    else {
      /* host_proc != myproc; receive hedge data from host_proc */
      MPI_Recv(hcnt, 2, MPI_INT, host_proc, 1, comm, &status);
      *nhedges = hcnt[0];
      *hgid = (int *) malloc(hcnt[0] * sizeof(int));
      *hindex = (int *) malloc((hcnt[0]+1) * sizeof(int));
      *hvertex = (int *) malloc(hcnt[1] * sizeof(int));
      *hvertex_proc = (int *) malloc(hcnt[1] * sizeof(int));
      if (*hewgt_dim)
        *hewgts = (float *) malloc(hcnt[0] * *hewgt_dim * sizeof(float));
      if ((hcnt[0] && !(*hgid)) || !(*hindex) ||
  	(hcnt[1] && (!(*hvertex) || !(*hvertex_proc))) ||
  	(hcnt[0] && *hewgt_dim && !(*hewgts))) {
        Gen_Error(0, "fatal: memory error in dist_hyperedges");
        return 0;
      }
      MPI_Recv(*hgid, hcnt[0], MPI_INT, host_proc, 2, comm, &status);
      MPI_Recv(*hindex, hcnt[0]+1, MPI_INT, host_proc, 3, comm, &status);
      MPI_Recv(*hvertex, hcnt[1], MPI_INT, host_proc, 4, comm, &status);
      MPI_Recv(*hvertex_proc, hcnt[1], MPI_INT, host_proc, 5, comm, &status);
      if (*hewgt_dim)
        MPI_Recv(*hewgts, hcnt[0]* *hewgt_dim, MPI_FLOAT, host_proc, 6, comm,
  	       &status);
    }
  
    DEBUG_TRACE_END(myproc, yo);
    return 1;
  }
  /*****************************************************************************/
  static int create_edge_lists(int nMyPins, ZOLTAN_ID_TYPE *myPinI, ZOLTAN_ID_TYPE *myPinJ,
        int *numHEdges, ZOLTAN_ID_TYPE **edgeGno, int **edgeIdx, ZOLTAN_ID_TYPE **pinGno)
  {
  int nedges, i, lid;
  int *count, *start, *idx;
  ZOLTAN_ID_TYPE *eidList, *pins, *match;
  
    *numHEdges = 0;
    *edgeGno = *pinGno = NULL;
    *edgeIdx = NULL;
  
    if (nMyPins == 0){
      *edgeIdx = (int *)malloc(sizeof(int));
      **edgeIdx = 0; /* number of pins */
      return 1;
    }
  
    eidList = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * nMyPins);
    pins = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * nMyPins);
    idx = (int *)malloc(sizeof(int) * nMyPins);
  
    if (!eidList || !idx || !pins){
      safe_free((void **)(void *)&eidList);
      safe_free((void **)(void *)&idx);
      safe_free((void **)(void *)&pins);
      Gen_Error(0, "memory allocation");
      return 0;
    }
  
    /* create a sorted list of unique edges IDs */
  
    memcpy(eidList, myPinI, sizeof(ZOLTAN_ID_TYPE) * nMyPins);
    qsort((void *)eidList, nMyPins, sizeof(ZOLTAN_ID_TYPE), _zoltan_sortFunc);
  
    for (i=1, nedges=1; i < nMyPins; i++){
      if (eidList[i] == eidList[nedges-1]) continue;
      if (nedges < i){
        eidList[nedges] = eidList[i];
      }
      nedges++;
    }
  
    eidList = (ZOLTAN_ID_TYPE *)realloc(eidList, nedges * sizeof(ZOLTAN_ID_TYPE));
  
    /* Count pins in each edge, map pins to local edge index */
  
    count = (int *)calloc(sizeof(int), nedges);
  
    if (!count){
      safe_free((void **)(void *)&eidList);
      safe_free((void **)(void *)&idx);
      safe_free((void **)(void *)&pins);
      Gen_Error(0, "memory allocation");
      return 0;
    }
  
    for (i = 0; i<nMyPins; i++){
      match = (ZOLTAN_ID_TYPE *)bsearch((const void *)(myPinI + i), (const void *)eidList,
  		    nedges, sizeof(ZOLTAN_ID_TYPE), _zoltan_sortFunc);
  
      if (!match){
        safe_free((void **)(void *)&eidList);
        safe_free((void **)(void *)&idx);
        safe_free((void **)(void *)&pins);
        safe_free((void **)(void *)&count);
        Gen_Error(0, "bsearch failure");
        return 0;
      }
  
      lid = (int)(match - eidList);
      count[lid]++;
      idx[i] = lid;
    }
  
    /* Create edges lists */
  
    start = (int *)malloc(sizeof(int) * (nedges+1));
    if (!start){
      safe_free((void **)(void *)&eidList);
      safe_free((void **)(void *)&idx);
      safe_free((void **)(void *)&pins);
      safe_free((void **)(void *)&count);
      Gen_Error(0, "memory allocation");
      return 0;
    }
    start[0] = 0;
    for (i=0; i<nedges; i++){
      start[i+1] = start[i] + count[i];
      count[i] = 0;
    }
  
    for (i=0; i<nMyPins; i++){
      lid = idx[i];
      pins[start[lid] + count[lid]] = myPinJ[i];
      count[lid]++;
    }
  
    free(count);
    free(idx);
  
    *numHEdges = nedges;
    *edgeGno   = eidList;
    *edgeIdx   = start;
    *pinGno    = pins;
  
    return 1;
  }
  
  /*****************************************************************************/
  
  static int process_mtxp_file(PARIO_INFO_PTR pio_info,
    char *filebuf, size_t fsize, int num_my_lines,
    int nprocs, int myrank,
    ZOLTAN_ID_TYPE *nGlobalEdges, ZOLTAN_ID_TYPE *nGlobalVtxs, 
    int *vtxWDim, int *edgeWDim,
    int *nMyPins, 
    ZOLTAN_ID_TYPE **myPinI, ZOLTAN_ID_TYPE **myPinJ,
    int *nMyVtx, ZOLTAN_ID_TYPE **myVtxNum, float **myVtxWgts,
    int *nMyEdgeWgts, ZOLTAN_ID_TYPE **myEdgeNum, float **myEdgeWgts,
    int preprocessed)
  {
  ZOLTAN_ID_TYPE nedges, nvtxs, npins, numew;
  ZOLTAN_ID_TYPE eid, vid, i;
  ZOLTAN_ID_TYPE myminPin=0, mymaxPin=0, myminVtx=0, mymaxVtx=0, myshare, share;
  ZOLTAN_ID_TYPE *myi, *myj, *myvno, *myeno;
  int ok, not_ok;
  int vdim, edim, nFileProcs;
  int countMyVtxs, countMyEdges, countMyPins;
  int proc, mine, nextpin, rc, counter;
  int nexte, nextv, nDistProcs=0;
  float pinVal;
  char *line, *token, *pinBuf, *vwgtBuf, *ewgtBuf;
  float *myvwgt, *myewgt;
  char cmesg[256];
  char linestr[MATRIX_MARKET_MAX_LINE+1];
  
    *nGlobalEdges = *nGlobalVtxs = 0;
    *myPinI = *myPinJ = *myVtxNum = *myEdgeNum = NULL;
    *nMyPins = *nMyVtx = *nMyEdgeWgts = 0;
    *vtxWDim = *edgeWDim = 0;
    *myVtxWgts = *myEdgeWgts = NULL;
  
    ok = 1;
    not_ok = 0;
  
    line = first_char(filebuf, fsize);
    if (line && (*line == COMMENT_CHAR)){
      line = next_line_of_data(line, fsize, linestr);  /* skip comments & blanks */
    }
  
    if (!line){
      sprintf(cmesg, "Truncated file\n");
      Gen_Error(0, cmesg);
      return not_ok;
    }
  
    rc = sscanf(linestr, ZOLTAN_ID_SPEC ZOLTAN_ID_SPEC ZOLTAN_ID_SPEC " %d %d " ZOLTAN_ID_SPEC "%d",
  	    &nedges, &nvtxs, &npins, &nFileProcs, &vdim, &numew, &edim);
  
    if (rc != 7){
      sprintf(cmesg, "%s\nFirst line should have 7 values in it\n",linestr);
      Gen_Error(0, cmesg);
      return not_ok;
    }
  
    *nGlobalEdges = nedges;
    *nGlobalVtxs = nvtxs;
    *vtxWDim = vdim;
    *edgeWDim = edim;
  
    if (preprocessed && (num_my_lines == 0)){
      return ok;
    }
  
    if (!preprocessed){
  
      myminPin = mymaxPin = -1;
      myminVtx = mymaxVtx = -1;
      nDistProcs = nprocs;
  
      if ((pio_info->init_dist_procs > 0) &&
          (pio_info->init_dist_procs < nprocs)){
        nDistProcs = pio_info->init_dist_procs;
      }
      
      if (pio_info->init_dist_type == INITIAL_LINEAR){ /* vertex distribution */
        if (myrank < nDistProcs){
          share = nvtxs / nDistProcs;
          i = nvtxs - (nDistProcs * share);
          myshare = ((myrank < (int)i) ? share+1 : share);
          myminVtx = myrank * myshare;
          if (myrank >= (int)i) myminVtx += i;
          mymaxVtx = myminVtx + myshare - 1;
        }
      }
      if (pio_info->init_dist_pins == INITIAL_LINEAR){ /* pin distribution */
        share = npins / nprocs;
        i = npins - (nprocs * share);
        myshare = ((myrank < (int)i) ? share+1 : share);
        myminPin = myrank * myshare;
        if (myrank >= (int)i) myminPin += i;
        mymaxPin = myminPin + myshare - 1;
      }
    }
  
    myvno = myeno = myi = myj = NULL;
    myewgt = myvwgt = NULL;
    pinBuf = vwgtBuf = ewgtBuf = NULL;
  
    /* Read through the pins, vertex weights and edge weights.
     * Accumulate all vertex and edge IDs, and map these to
     * consecutive global numbers beginning with zero.
     *
     * Also count my pins, my vertices, and the number of edges
     * for which I provide weights.
     */
  
    countMyPins = 0;
    countMyVtxs = 0;
    countMyEdges = 0;
    nexte = nextv = 0;
  
    section_starts(line, &pinBuf, &vwgtBuf, &ewgtBuf);
  
    if (preprocessed){
  
      if (pinBuf){
        line = pinBuf;
        while (line){
          countMyPins++;
          line = next_line(line, fsize);
          if (line && line[0] == COMMENT_CHAR) break;
        }
      }
  
      if (vwgtBuf){
        line = vwgtBuf;
        while (line){
          countMyVtxs++;
          line = next_line(line, fsize);
          if (line && line[0] == COMMENT_CHAR) break;
        }
      }
  
      if (ewgtBuf){
        line = ewgtBuf;
        while (line){
          countMyEdges++;
          line = next_line(line, fsize);
          if (line && line[0] == COMMENT_CHAR) break;
        }
      }
    }
    else{             /* file is not preprocessed */
  
      line = pinBuf;
      counter = 0;
      /* Skip any additional comment lines before pins begin
       * Zoltan_Generate_Files adds an extra comment line here to mtxp files.
       */
      while (line) {
        if (line[0] != COMMENT_CHAR)
          break;
        line = next_line(line, fsize);
      }
       
      while (line){          /* PINS */
  
        make_string(line, linestr);
        rc = sscanf(linestr, ZOLTAN_ID_SPEC ZOLTAN_ID_SPEC "%f %d", &eid, &vid, &pinVal, &proc);
  
        if ((rc != 4) || (eid < 1) || (eid > nedges) ||
  	  (vid < 1) || (vid > nvtxs) || (proc < 0) || (proc >= nFileProcs)){
          sprintf(cmesg,"%s\nlooking for \"edge vertex pin process\"\n",linestr);
          Gen_Error(0, cmesg);
          goto failure;
        }
  
        eid -= 1;
        vid -= 1;
        mine = my_pin(eid, vid, proc, counter++, npins,
           myminPin, mymaxPin, myrank, nprocs, pio_info);
  
        if (mine){
          countMyPins++;
        }
  
        line = next_line(line, fsize);
        if (line && line[0] == COMMENT_CHAR) break;
      }
  
      line = vwgtBuf;
      /* Skip any additional comment lines before vwgts begin
       * Zoltan_Generate_Files adds an extra comment line here to mtxp files.
       */
      while (line) {
        if (line[0] != COMMENT_CHAR)
          break;
        line = next_line(line, fsize);
      }
  
      while(line) {        /* VERTICES and possibly WEIGHTS */
    
        make_string(line, linestr);
        rc = sscanf(linestr, ZOLTAN_ID_SPEC, &vid);
  
        token = get_nth_token(linestr, vdim + 1, strlen(linestr), 1, (char)0);
        if (token) proc = atoi(token);
  
        if ((rc != 1) || !token ||
    	 (vid < 1) || (vid > nvtxs) || (proc < 0) || (proc >= nFileProcs)){
          sprintf(cmesg,
          "%s\nlooking for \"vertex {optional weights} process\"\n",linestr);
          Gen_Error(0, cmesg);
          goto failure;
        }
    
        vid -= 1;
        mine = my_vtx(proc, vid, myminVtx, mymaxVtx, myrank, nDistProcs, pio_info);
    
        if (mine){
          countMyVtxs++;
        }
        line = next_line(line, fsize);
        if (line && line[0] == COMMENT_CHAR) break;
      }
  
      if (numew > 0){                      /* HYPEREDGE WEIGHTS */
        line = ewgtBuf;
        /* Skip any additional comment lines before ewgts begin
         * Zoltan_Generate_Files adds an extra comment line here to mtxp files.
         */
        while (line) {
          if (line[0] != COMMENT_CHAR)
            break;
          line = next_line(line, fsize);
        }
  
        while(line) {   
          make_string(line, linestr);
          rc = sscanf(linestr, ZOLTAN_ID_SPEC, &eid);
          token = get_nth_token(linestr, edim + 1, strlen(linestr), 1, (char)0);
  
          if (token) proc = atoi(token);
          if ((rc != 1) || !token ||
    	  (eid < 1) || (eid > nedges) || (proc < 0) || (proc >= nFileProcs)){
    	  sprintf(cmesg,
    	  "%s\nlooking for \"edge {optional weights} process\"\n",linestr);
    	  Gen_Error(0, cmesg);
    	  goto failure;
          }
          proc = atoi(token);
    
          if ((proc % nprocs) == myrank){
    	  countMyEdges++;
          }
  
          line = next_line(line, fsize);
          if (line && line[0] == COMMENT_CHAR) break;
        }
      }
    } 
  
    /* Start over at beginning of file and save my pins, and weights */
  
    mine = 1;
  
    if (countMyPins > 0){
      myi = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * countMyPins);
      myj = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * countMyPins);
      if (!myi || !myj){
        sprintf(cmesg,"memory allocation\n");
        Gen_Error(0, cmesg);
        goto failure;
      }
      nextpin = 0;
  
      make_string(pinBuf, linestr);
      line = pinBuf;
      counter = 0;
  
      while(line){
  
        sscanf(linestr, ZOLTAN_ID_SPEC ZOLTAN_ID_SPEC "%f %d", &eid, &vid, &pinVal, &proc);
  
        eid -= 1;
        vid -= 1;
  
        if (!preprocessed)
          mine = my_pin(eid, vid, proc, counter++, npins,
  		  myminPin, mymaxPin, myrank, nprocs, pio_info);
  
        if (mine){
  	myi[nextpin] = eid;
  	myj[nextpin] = vid;
  	nextpin++;
        }
  
        if (nextpin == countMyPins) break;
        line = next_line_of_data(line, fsize, linestr);
      }
    }
  
    if (countMyVtxs){
      myvno = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * countMyVtxs);
      if (vdim > 0){
        myvwgt = (float *)malloc(sizeof(float) * countMyVtxs * vdim);
      }
      if (!myvno || (vdim && !myvwgt)){
        sprintf(cmesg,"memory allocation\n");
        Gen_Error(0, cmesg);
        goto failure;
      }
      nextv = 0;
  
      make_string(vwgtBuf, linestr);
      line = vwgtBuf;
      counter = 0;
  
      while(line){
  
        sscanf(linestr, ZOLTAN_ID_SPEC, &vid);
        vid -= 1;
  
        if (!preprocessed){
          token = get_nth_token(linestr, vdim + 1, strlen(linestr), 1, (char)0);
          proc = atoi(token);
          mine = my_vtx(proc, vid, myminVtx, mymaxVtx, myrank, nDistProcs, pio_info);
        }
  
        if (mine){
          int jj;
  	myvno[nextv] = vid;
  	for (jj=0; jj<vdim; jj++){
  	  token = get_nth_token(linestr, 1 + jj, strlen(linestr), 1, (char)0);
  	  if (!token){
  	    sprintf(cmesg,"%s\nCan't find %d vertex weights\n",linestr,vdim);
  	    Gen_Error(0, cmesg);
  	    goto failure;
  	  }
  	  myvwgt[nextv*vdim + jj] = (float)atof(token);
  	}
  	nextv++;
        }
        if (nextv == countMyVtxs) break;
  
        line = next_line_of_data(line, fsize, linestr);
      }
    }
  
    if (countMyEdges > 0){
      myeno = (ZOLTAN_ID_TYPE *)malloc(sizeof(ZOLTAN_ID_TYPE) * countMyEdges);
      myewgt = (float *)malloc(sizeof(float) * countMyEdges * edim);
      if (!myeno || !myewgt){
        sprintf(cmesg,"memory allocation\n");
        Gen_Error(0, cmesg);
        goto failure;
      }
      nexte = 0;
  
      make_string(ewgtBuf, linestr);
      line = ewgtBuf;
  
      while (line){
        sscanf(linestr, ZOLTAN_ID_SPEC, &eid);
        eid -= 1;
  
        if (!preprocessed){
          mine = 0;
          token = get_nth_token(linestr, edim + 1, strlen(linestr), 1, (char)0);
          proc = atoi(token);
          if ((proc % nprocs) == myrank)
            mine = 1;
        }
  
        if (mine){
          int jj;
  	myeno[nexte] = eid;
  	for (jj=0; jj<edim; jj++){
  	  token = get_nth_token(linestr, 1 + jj, strlen(linestr), 1, (char)0);
  	  if (!token){
  	    sprintf(cmesg,"%s\nCan't find %d edge weights\n",linestr,edim);
  	    Gen_Error(0, cmesg);
  	    goto failure;
  	  }
  	  myewgt[nexte*edim + jj] = (float)atof(token);
  	}
  	nexte++;
        }
        if (nexte == countMyEdges) break;
        line = next_line_of_data(line, fsize, linestr);
      }
    }
  
    rc = 1;   /* success */
    goto done;
  
  failure:
    if (myvno) free(myvno);
    if (myvwgt) free(myvwgt);
    if (myeno) free(myeno);
    if (myewgt) free(myewgt);
    if (myi) free(myi);
    if (myj) free(myj);
    nedges = nvtxs = vdim = edim = 0;
    countMyPins = countMyVtxs = countMyEdges = 0;
    rc = 0;
  
  done:
  
    *nGlobalEdges = nedges;
    *nGlobalVtxs = nvtxs;
    *vtxWDim = vdim;
    *edgeWDim = edim;
  
    *nMyPins = countMyPins;
    *myPinI  = myi;
    *myPinJ  = myj;
  
    *nMyVtx = countMyVtxs;
    *myVtxNum = myvno;
    *myVtxWgts = myvwgt;
  
    *nMyEdgeWgts = countMyEdges;
    *myEdgeNum = myeno;
    *myEdgeWgts = myewgt;
  
    return rc;
  }
  
  /* Read the mtxp file and return only comments plus lines I "own",
   * null-terminate the buffer, and set the number of objects I own. 
   * Return the size of the buffer, 0 on failure.
   */
  
  int read_mtxp_lines(char *fname, int my_rank, int nprocs, char **mine, int *num)
  {
  off_t fsize=0, bytes_read;
  struct stat statbuf;
  size_t inbufsize, outbufsize, nbytes;
  char *inbuf=NULL, *outbuf=NULL, *c;
  char *c_in, *c_out, *c_end;
  char *c_in_end;
  char *buf;
  long count_mine;
  int rc, max_sanity, total_line_found, line_size=0;
  ZOLTAN_ID_TYPE nedges, nvtxs, npins, numew; 
  int numOwners, vdim, edim;
  int i=0;
  int owning_proc;
  int status;
  int *new_owner=NULL;
  FILE *fp=NULL;
  
    *mine = NULL;
    *num = 0;
  
    rc = stat(fname, &statbuf);
  
    if (rc){
      status = 0;
      goto End;
    }
    
    fsize = statbuf.st_size;
    fp = fopen(fname, "r");
    if (!fp){
      status = 0;
      goto End;
    }
  
    outbufsize = fsize / (nprocs - 1);
    outbuf = (char *)malloc(outbufsize+1);
  
    if (!outbuf){
      status = 0;
      goto End;
    }
  
  #if PRINT_DEBUG_INFO
    if (fsize > 500){
      inbufsize = 500;
      inbuf = malloc(inbufsize+1);
  
      if (!inbuf){
        inbufsize *= .75;
        inbuf = malloc(inbufsize+1);
    
        if (!inbuf){
          inbufsize *= .5;
          inbuf = malloc(inbufsize+1);
          if (!inbuf){
            status = 0;
            goto End;
          }
        }
      }
    }
  #else
    if (fsize > 10*1024){
  
      /* TODO a Zoltan utility that uses hwloc info to decide a reasonable amount of
       *     memory for Zoltan to use.  Maybe more of a TO_CONSIDER.
       */
      
      inbufsize = (fsize > 100*1024 ? 100*1024 : fsize);
      inbuf = malloc(inbufsize+1);
  
      if (!inbuf){
        inbufsize *= .75;
        inbuf = malloc(inbufsize+1);
    
        if (!inbuf){
          inbufsize *= .5;
          inbuf = malloc(inbufsize+1);
          if (!inbuf){
            status = 0;
            goto End;
          }
        }
      }
    }
  #endif
    else{
      inbufsize = fsize;
      inbuf = malloc(inbufsize);
      if (!inbuf){
        status = 0;
        goto End;
      }
    }
  
    c_out = outbuf;
    max_sanity = MATRIX_MARKET_MAX_LINE+1;
    total_line_found=0;   /* totals at the top of file */
    bytes_read = 0;
    count_mine = 0;
    nbytes = 0;
  
    while (1){
      /* read next chunk */
      size_t rrc;
      rrc = fread(inbuf, 1, inbufsize, fp);
      if (rrc < inbufsize){
        if ((rrc == 0) && feof(fp)) break;
        if (ferror(fp)){
          status = 0;
          goto End;
        }
      }
  
      bytes_read += rrc;
  
      if ((bytes_read == fsize) && (inbuf[rrc-1] != '\n')){
        /* we assume last character in the file is new line */
        inbuf[rrc++] = '\n';
      }
  
      c_in = inbuf;
      c_in_end = inbuf + rrc;
  
      while (c_in < c_in_end){
        c_in = first_char(c_in, max_sanity);  /* skip blanks */
  
        c_end = c_in;
        line_size = 0;
  
        while ((c_end < c_in_end) && (*c_end != '\n') && (line_size < max_sanity)){
          c_end++;
          line_size++;
        }
  
        if (line_size == max_sanity){
          status = 0;
          goto End;
        }
  
        if (c_end == c_in_end){         /* file ends in mid-line */
          line_size = c_in_end - c_in;
          break;
        }
  
        *c_end = 0;                /* null terminate the line */
  
        line_size++;               /* plus '\n' */
  
        if (line_size > 1){
  
          if (!total_line_found){
            if (isdigit(c_in[0])){
              total_line_found=1;
              rc = sscanf(c_in, ZOLTAN_ID_SPEC ZOLTAN_ID_SPEC ZOLTAN_ID_SPEC " %d %d " ZOLTAN_ID_SPEC "%d",
  	      &nedges, &nvtxs, &npins, &numOwners, &vdim, &numew, &edim);
              if (rc != 7){
                status = 0;
                goto End;
              }
              if (numOwners > nprocs){
                new_owner = (int *)malloc(sizeof(int) * numOwners);
                for (i=0; i < numOwners; i++){
                  new_owner[i] = i % nprocs;
                }
              }
            }
          }
          else if (c_in[0] != COMMENT_CHAR){
            if (!total_line_found){
              status = 0;
              goto End;
            }
            c = get_nth_token(c_in, 0, max_sanity, -1, (char)0);
            if (c){
              owning_proc = atoi(c);
            }
            else{
              status = 0;
              goto End;
            }
  
            if ((owning_proc == my_rank) ||
                (new_owner && new_owner[owning_proc] == my_rank)){
              count_mine++;
            }
            else{
              line_size = 0;
            }
          }
  
          if (line_size > 0){
  
            /* Line is comment, totals at top, or one of mine */
  
            if (nbytes + line_size > outbufsize){
                outbufsize += line_size;
                outbufsize *= 1.2;
    
                buf = (char *)malloc(outbufsize+1);
                if (!buf){
                  status = 0;
                  goto End;
                }
                memcpy(buf, outbuf, nbytes);
                free(outbuf);
                c_out = buf + nbytes;
                outbuf = buf;
            }
    
            sprintf(c_out, "%s\n", c_in);
            c_out += line_size;
            nbytes += line_size;
          }
        }
  
        c_in = c_end+1;
      }
  
      if ((c_in < c_in_end) && (line_size > 0)){
        /* rewind fp to start of incomplete line */
        fseek(fp, -line_size, SEEK_CUR);
      }
    }
  
    *c_out++ = 0;   /* null terminate buffer */
  
    status = c_out - outbuf;
  
    *mine = outbuf;
    *num = count_mine;
  
  End:
    if (fp) fclose(fp);
    if (new_owner) free(new_owner);
    if (inbuf) free(inbuf);
    if (outbuf && (*mine != outbuf)) free(outbuf);
    return status;
  }
  
  /*****************************************************************************
     functions to help read the .mtxp file
  
  ******************************************************************************/
  
  static char *get_nth_token(char *line, int nth, /* get nth token (0-based) */
                           size_t max, 
                           int direction,      /* 1: from the front, -1: from the back */
                           char terminator)    /* null or \n terminated */
  {
  char *c1, *c2;
  char *loc[10];    /* assume nth is at most 9 */
  size_t l;
  int found;
  
    if (nth > 9){
      return NULL;
      /* TODO - error */
    }
  
    if ((c2 = c1 = first_char(line, max)) == NULL) return NULL;
  
    l = (size_t)(c1-line);
  
    while ((*c2 != terminator) && (l < max)){ c2++; l++;} 
  
    if ((c2 == c1) ||            /* no tokens */
        (*c2 != terminator))     /* invalid line */
      return NULL;
  
    if (c2 == c1 + 1){  /* only one token in line */
      if (nth == 0)
        return c1;
      else
        return NULL;
    }
  
    c2--;
  
    while ((c2 > c1) && IS_BLANK(*c2)) c2--; 
    *(c2+1) = 0;
  
    found = 0;     /* null terminate last token */
  
    while (*c1){
      loc[found++] = c1++;
      while (*c1 && !IS_BLANK(*c1)) c1++;
      while (*c1 && IS_BLANK(*c1)) c1++;
    }
  
    if (nth < found){
      if (direction == 1)
        c1 = loc[nth];
      else
        c1 = loc[found - nth - 1];
    }
    else
      c1 = NULL;
    
    return c1;
  }
  
  static char *first_char(char *buf, size_t max)
  {
  char *c = buf;
  size_t sanity = 0;
  
    while (*c && isspace(*c) && (sanity++ < max)) c++;
  
    if ((sanity > max) || !*c) return NULL;
  
    return c;
  }
  
  static void make_string(char *buf, char *str)
  {
    *str = 0;
  
    /* replace new line character with NULL character */
  
    while (*buf && (*buf != '\n')){
      *str++ = *buf++;
    }
  
    *str = 0;
  }
  
  static void section_starts(char *buf, char **pins, char **vwgts, char **ewgts)
  {
  char *c, line[MATRIX_MARKET_MAX_LINE+1];
  char *pin_string="Edge and Vertex IDs";  /* These strings are literal             */
  char *vwgt_string="Vertex weights";      /* text from mtxp file.  If file changes */
  char *ewgt_string="Edge weights";        /* this text has to change too.          */
  
    *pins = NULL;
    *vwgts = NULL;
    *ewgts = NULL;
  
    if (!buf) return;
  
    /* Find pointers to start of pin section, vertex weight section, edge weight section.
     * It's not an error if one or more of these are missing.
     */
  
    c = buf;
  
    while (c){
      c = next_line_of_comment(c, MATRIX_MARKET_MAX_LINE, line);
      if (c){
        if (strstr(line, pin_string)){
          c = *pins = next_line_of_data(c, MATRIX_MARKET_MAX_LINE, NULL);
        }
        else if (strstr(line, vwgt_string)){
          c = *vwgts = next_line_of_data(c, MATRIX_MARKET_MAX_LINE, NULL);
        }
        else if (strstr(line, ewgt_string)){
          c = *ewgts = next_line_of_data(c, MATRIX_MARKET_MAX_LINE, NULL);
        }
      }
    }
  
    return;
  }
  
  static char *next_line(char *buf, size_t max)
  {
  char *c = buf;
  size_t sanity = 0;
  
    /* Skip current line, and any subsequent blank lines.
     * Return pointer to the next line. 
     */
  
    if (!buf) return NULL;
  
    /* go to end of line */
    while (*c && (*c != '\n') && (sanity++ < max)) c++;
  
    if (*c==0 || sanity == max || *(c+1) == 0)
      return NULL;
  
    c = first_char(c+1, max);
  
    return c;
  }
  
  static char *next_line_of_data(char *buf, size_t max, char *str)
  {
  char *c; 
  
    /* Skip current line, and any following comments or blank lines.
     * Return pointer to the next line. Also copy
     * the line to a null-terminated C string because sscanf
     * may perform really poorly on very long strings.
     */
  
    if (!buf) return NULL;
  
    if (str) *str = 0;
  
    c = buf;
  
    while (c){
      c = next_line(c, max);
      if (c && (c[0] != COMMENT_CHAR)){
        if (str) make_string(c, str);
        return c;
      }
    }
  
    return NULL;
  }
  
  static char *next_line_of_comment(char *buf, size_t max, char *str)
  {
  char *c; 
  
    /* Skip current line, and any following data or blank lines.
     * Return pointer to the next line. Also copy
     * the line to a null-terminated C string because sscanf
     * may perform really poorly on very long strings.
     */
  
    if (!buf) return NULL;
  
    if (str) *str = 0;
  
    c = buf;
  
    while (c){
      c = next_line(c, max);
      if (c && (c[0] == COMMENT_CHAR)){
        if (str) make_string(c, str);
        return c;
      }
    }
  
    return NULL;
  }
  
  /*************************************************************************/
  
  static int my_vtx(int proc, 
         ZOLTAN_ID_TYPE vtx, ZOLTAN_ID_TYPE mymin, ZOLTAN_ID_TYPE mymax,
         int myrank, int nprocs, PARIO_INFO_PTR pio_info)
  {
    int mine = -1;
  
    if (myrank >= nprocs) return 0;
    if (nprocs == 1) return 1;
  
    if (pio_info->init_dist_type == INITIAL_FILE){
      /* The process ID of the vertex is in the file */
      mine = ((proc % nprocs) == myrank);
    }
    else if (pio_info->init_dist_type == INITIAL_CYCLIC){
      /* Deal out the vertices in a random fashion */
      mine = ((int)(vtx % nprocs) == myrank);
    }
    else if (pio_info->init_dist_type == INITIAL_LINEAR){
      /* First process gets first nvtxs/nprocs vertices, and so on */
      mine = ((vtx >= mymin) && (vtx <= mymax));
    }
  
    return mine;
  }
  static int my_pin(ZOLTAN_ID_TYPE eid, ZOLTAN_ID_TYPE vid, int proc,
         ZOLTAN_ID_TYPE pin, ZOLTAN_ID_TYPE npins, 
         ZOLTAN_ID_TYPE mymin, ZOLTAN_ID_TYPE mymax,
         int myrank, int nprocs, PARIO_INFO_PTR pio_info)
  {
    int mine = -1;
  
    if (nprocs == 1) return 1;
  
    if (pio_info->init_dist_pins == INITIAL_ZERO){
      /* Node zero initially has all pins */
      mine = (myrank == 0);
    }
    else if (pio_info->init_dist_pins == INITIAL_FILE){
      /* The process ID of the pin owner is in the file */
      mine = ((proc % nprocs) == myrank);
    }
    else if (pio_info->init_dist_pins == INITIAL_CYCLIC){
      /* Deal out the pins in a random fashion */
      mine = ((int)(pin % nprocs) == myrank);
    }
    else if (pio_info->init_dist_pins == INITIAL_LINEAR){
      /* First process gets first npins/nprocs pins, and so on */
      mine = ((pin >= mymin) && (pin <= mymax));
    }
    else if (pio_info->init_dist_pins == INITIAL_ROW){
      /* Each process gets entire rows (hyperedges) of pins, no
         row is split across processes  */
  
      mine = ((int)(eid % nprocs) == myrank);
    }
    else if (pio_info->init_dist_pins == INITIAL_COL){
      /* Each process gets entire columns of pins, no column is split
         across processes  */
      mine = ((int)(vid % nprocs) == myrank);
    }
  
    return mine;
  }
  /*************************************************************************/
  static int _zoltan_sortFunc(const void *a, const void *b)
  {
    ZOLTAN_ID_TYPE ia, ib;
  
    ia = *(ZOLTAN_ID_TYPE *)a;
    ib = *(ZOLTAN_ID_TYPE *)b;
  
    if (ia < ib){
      return -1;
    }
    else if (ia > ib){
      return 1;
    }
    else{
      return 0;
    }
  }
  
  /*************************************************************************/
  #if PRINT_DEBUG_INFO
  static void debug_elements(int Proc, int Num_Proc, int num, ELEM_INFO_PTR el)
  {
    int i,e;
    for (i=0; i<Num_Proc; i++){
      if (i == Proc){
        printf("Process %d (%d elements):\n",i,num);
        for (e=0; e<num; e++){
  	if (e%20==0) printf("\n    ");
  	printf("%" ZOLTAN_ID_SPEC,el[e].globalID);
        }
        printf("\n");
        fflush(stdout);
      }
      MPI_Barrier(MPI_COMM_WORLD);
    }
  }
  static void debug_lists(int Proc, int Num_Proc, int nedge, int *index, ZOLTAN_ID_TYPE *vtx, int *vtx_proc, ZOLTAN_ID_TYPE *egid)
  {
    int i,e,v,nvtxs;
    for (i=0; i<Num_Proc; i++){
      if (i == Proc){
        printf("Process %d\n",i);
        for (e=0; e<nedge; e++){
  	nvtxs = index[e+1]-index[e];
  	printf("%" ZOLTAN_ID_SPEC " ) ",egid[e]);
  	for (v=0; v<nvtxs; v++){
  	  if (v && (v%10==0)) printf("\n    ");
  	  printf("%" ZOLTAN_ID_SPEC " (%d) ",*vtx++,*vtx_proc++);
  	}
  	printf("\n");
        }
        fflush(stdout);
      }
      MPI_Barrier(MPI_COMM_WORLD);
    }
  }
  static void debug_pins(int Proc, int Num_Proc,  
            ZOLTAN_ID_TYPE nGlobalEdges, ZOLTAN_ID_TYPE nGlobalVtxs, 
            int vtxWDim, int edgeWDim,
            int nMyPins, ZOLTAN_ID_TYPE *myPinI, ZOLTAN_ID_TYPE *myPinJ,
            int nMyVtx, ZOLTAN_ID_TYPE *myVtxNum, float *myVtxWgts,
            int nMyEdgeWgts, ZOLTAN_ID_TYPE *myEWGno, float *myEdgeWgts)
  {
  int p,i,j,k;
  
    MPI_Barrier(MPI_COMM_WORLD);
    for (p=0; p < Num_Proc; p++){
      if (p == Proc){
        printf("Process: %d\n",p);
        printf(ZOLTAN_ID_SPEC " global edges, " ZOLTAN_ID_SPEC " global vertices, vwgtdim %d, ewgtdim %d\n",
                nGlobalEdges, nGlobalVtxs, vtxWDim, edgeWDim);
        printf("%d pins: ",nMyPins);
        for (i=0; i < nMyPins; i++) printf("%d,%d ",myPinI[i],myPinJ[i]);
        printf("\n");
        printf("%d vertices: ",nMyVtx);
        for (i=0, j=0; i < nMyVtx; i++){
          printf(ZOLTAN_ID_SPEC " ",myVtxNum[i]);
          for (k=0; k < vtxWDim; k++){
            printf("%f", myVtxWgts[j++]);
            if (k < vtxWDim-1) printf(",");
          }
          if (vtxWDim > 0) printf(" ");
        }
        printf("\n");
        printf("%d edge weights: ",nMyEdgeWgts);
        for (i=0, j=0; i < nMyEdgeWgts; i++){
          printf(ZOLTAN_ID_SPEC " ",myEWGno[i]);
          for (k=0; k < edgeWDim; k++){
            printf("%f", myEdgeWgts[j++]);
            if (k < edgeWDim-1) printf(",");
          }
          if (edgeWDim > 0) printf(" ");
        }
        printf("\n");
        fflush(stdout);
      } 
      MPI_Barrier(MPI_COMM_WORLD);
    }
    MPI_Barrier(MPI_COMM_WORLD);
  }
  #endif
  