text: /*
   * Copyright (C) by Argonne National Laboratory
   *     See COPYRIGHT in top-level directory
   */
  
  #include "adio.h"
  #include "adio_extern.h"
  
  #ifdef MPL_USE_DBG_LOGGING
  #define FLATTEN_DEBUG 1
  #endif
  
  static ADIOI_Flatlist_node *flatlist_node_new(MPI_Datatype datatype, MPI_Count count)
  {
      ADIOI_Flatlist_node *flat;
      flat = ADIOI_Malloc(sizeof(ADIOI_Flatlist_node));
  
      flat->type = datatype;
      flat->blocklens = NULL;
      flat->indices = NULL;
      flat->lb_idx = flat->ub_idx = -1;
      flat->refct = 1;
      flat->count = count;
      flat->flag = 0;
  
      flat->blocklens = (ADIO_Offset *) ADIOI_Calloc(flat->count * 2, sizeof(ADIO_Offset));
      flat->indices = flat->blocklens + flat->count;
      return flat;
  }
  
  /*
   * I don't really expect this to ever trigger, but without the below safety
   * valve, the design relies on the Count function coming out >= whatever
   * the Flatten function comes up with.  There are enough differences between
   * the two that it's hard to be positive this will always be true.  So every
   * time something's added to flat's arrays, let's make sure they're big enough
   * and re-alloc if not.
   */
  static void flatlist_node_grow(ADIOI_Flatlist_node * flat, int idx)
  {
      if (idx >= flat->count) {
          ADIO_Offset *new_blocklens;
          ADIO_Offset *new_indices;
          int new_count = (flat->count * 1.25 + 4);
          new_blocklens = (ADIO_Offset *) ADIOI_Calloc(new_count * 2, sizeof(ADIO_Offset));
          new_indices = new_blocklens + new_count;
          if (flat->count) {
              memcpy(new_blocklens, flat->blocklens, flat->count * sizeof(ADIO_Offset));
              memcpy(new_indices, flat->indices, flat->count * sizeof(ADIO_Offset));
              ADIOI_Free(flat->blocklens);
          }
          flat->blocklens = new_blocklens;
          flat->indices = new_indices;
          flat->count = new_count;
      }
  }
  
  void ADIOI_Optimize_flattened(ADIOI_Flatlist_node * flat_type);
  /* flatten datatype and add it to Flatlist */
  ADIOI_Flatlist_node *ADIOI_Flatten_datatype(MPI_Datatype datatype)
  {
      MPI_Count flat_count, curr_index = 0;
      int is_contig, flag;
      ADIOI_Flatlist_node *flat;
  
      if (ADIOI_Flattened_type_keyval == MPI_KEYVAL_INVALID) {
          /* ADIOI_End_call will take care of cleanup */
          MPI_Type_create_keyval(ADIOI_Flattened_type_copy,
                                 ADIOI_Flattened_type_delete, &ADIOI_Flattened_type_keyval, NULL);
      }
  
      /* check if necessary to flatten. */
  
      /* has it already been flattened? */
      MPI_Type_get_attr(datatype, ADIOI_Flattened_type_keyval, &flat, &flag);
      if (flag) {
  #ifdef FLATTEN_DEBUG
          DBG_FPRINTF(stderr, "ADIOI_Flatten_datatype:: found datatype %#X\n", datatype);
  #endif
          return flat;
      }
  
      /* is it entirely contiguous? */
      ADIOI_Datatype_iscontig(datatype, &is_contig);
  
  #ifdef FLATTEN_DEBUG
      DBG_FPRINTF(stderr, "ADIOI_Flatten_datatype:: is_contig %#X\n", is_contig);
  #endif
      /* it would be great if ADIOI_Count_contiguous_blocks and the rest of the
       * flattening code operated on the built-in named types, but
       * it recursively processes types, stopping when it hits a named type. So
       * we will do the little bit of work that named types require right here,
       * and avoid touching the scary flattening code. */
  
      if (is_contig)
          flat_count = 1;
      else {
          flat_count = ADIOI_Count_contiguous_blocks(datatype, &curr_index);
      }
      /* flatten and add to datatype */
      flat = flatlist_node_new(datatype, flat_count);
      if (is_contig) {
          MPI_Type_size_x(datatype, &(flat->blocklens[0]));
          flat->indices[0] = 0;
      } else {
  
          curr_index = 0;
          ADIOI_Flatten(datatype, flat, 0, &curr_index);
  #ifdef FLATTEN_DEBUG
          DBG_FPRINTF(stderr, "ADIOI_Flatten_datatype:: ADIOI_Flatten\n");
  #endif
  
  /*
   * Setting flat->count to curr_index, since curr_index is the most fundamentally
   * correct updated value that represents what's in the indices/blocklens arrays.
   * It would be nice if the counter function and the flatten function were in sync,
   * but the numerous cases that decrement flat->count in the flatten function show
   * that syncing them is a hack, and as long as the counter doesn't under-count
   * it's good enough.
   */
          flat->count = curr_index;
  
          ADIOI_Optimize_flattened(flat);
  /* debug */
  #ifdef FLATTEN_DEBUG
          {
              int i;
              for (i = 0; i < flat->count; i++) {
                  DBG_FPRINTF(stderr,
                              "ADIOI_Flatten_datatype:: i %#X, blocklens %#llX, indices %#llX\n", i,
                              flat->blocklens[i], flat->indices[i]);
              }
          }
  #endif
      }
      MPI_Type_set_attr(datatype, ADIOI_Flattened_type_keyval, flat);
      return flat;
  
  }
  
  /* ADIOI_Flatten()
   *
   * Assumption: input datatype is not a basic!!!!
   */
  void ADIOI_Flatten(MPI_Datatype datatype, ADIOI_Flatlist_node * flat,
                     ADIO_Offset st_offset, MPI_Count * curr_index)
  {
      int k, m, n, is_hindexed_block = 0;
      int lb_updated = 0;
      int combiner, old_combiner, old_is_contig;
      int nints, nadds, ntypes, old_nints, old_nadds, old_ntypes;
      /* By using ADIO_Offset we preserve +/- sign and
       * avoid >2G integer arithmetic problems */
      ADIO_Offset top_count;
      MPI_Count i, j, old_size, prev_index, basic_num, num, nonzeroth;
      MPI_Aint old_extent;        /* Assume extents are non-negative */
      int *ints;
      MPI_Aint *adds;             /* Make no assumptions about +/- sign on these */
      MPI_Datatype *types;
      MPI_Type_get_envelope(datatype, &nints, &nadds, &ntypes, &combiner);
      ints = (int *) ADIOI_Malloc((nints + 1) * sizeof(int));
      adds = (MPI_Aint *) ADIOI_Malloc((nadds + 1) * sizeof(MPI_Aint));
      types = (MPI_Datatype *) ADIOI_Malloc((ntypes + 1) * sizeof(MPI_Datatype));
      MPI_Type_get_contents(datatype, nints, nadds, ntypes, ints, adds, types);
  
  #ifdef FLATTEN_DEBUG
      DBG_FPRINTF(stderr, "ADIOI_Flatten:: st_offset %#llX, curr_index %#llX\n", st_offset,
                  *curr_index);
      DBG_FPRINTF(stderr, "ADIOI_Flatten:: nints %#X, nadds %#X, ntypes %#X\n", nints, nadds, ntypes);
      for (i = 0; i < nints; ++i) {
          DBG_FPRINTF(stderr, "ADIOI_Flatten:: ints[%lld]=%#X\n", (long long) i, ints[i]);
      }
      for (i = 0; i < nadds; ++i) {
          DBG_FPRINTF(stderr, "ADIOI_Flatten:: adds[%lld]=" MPI_AINT_FMT_HEX_SPEC "\n",
                      (long long) i, adds[i]);
      }
      for (i = 0; i < ntypes; ++i) {
          DBG_FPRINTF(stderr, "ADIOI_Flatten:: types[%lld]=%#llX\n", (long long) i,
                      (unsigned long long) (unsigned long) types[i]);
      }
  #endif
      /* Chapter 4, page 83: when processing datatypes, note this item from the
       * standard:
       Most datatype constructors have replication count or block length
       arguments.  Allowed values are non-negative integers. If the value is
       zero, no elements are generated in the type map and there is no effect
       on datatype bounds or extent.  */
  
      switch (combiner) {
  #ifdef MPIIMPL_HAVE_MPI_COMBINER_DUP
          case MPI_COMBINER_DUP:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_DUP\n");
  #endif
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  ADIOI_Flatten(types[0], flat, st_offset, curr_index);
              break;
  #endif
  #ifdef MPIIMPL_HAVE_MPI_COMBINER_SUBARRAY
          case MPI_COMBINER_SUBARRAY:
              {
                  int dims = ints[0];
                  MPI_Datatype stype;
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_SUBARRAY\n");
  #endif
  
                  ADIO_Type_create_subarray(dims, &ints[1],       /* sizes */
                                            &ints[dims + 1],      /* subsizes */
                                            &ints[2 * dims + 1],  /* starts */
                                            ints[3 * dims + 1],   /* order */
                                            types[0],     /* type */
                                            &stype);
                  ADIOI_Flatten(stype, flat, st_offset, curr_index);
                  MPI_Type_free(&stype);
              }
              break;
  #endif
  #ifdef MPIIMPL_HAVE_MPI_COMBINER_DARRAY
          case MPI_COMBINER_DARRAY:
              {
                  int dims = ints[2];
                  MPI_Datatype dtype;
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_DARRAY\n");
  #endif
  
                  ADIO_Type_create_darray(ints[0],        /* size */
                                          ints[1],        /* rank */
                                          dims, &ints[3], /* gsizes */
                                          &ints[dims + 3],        /* distribs */
                                          &ints[2 * dims + 3],    /* dargs */
                                          &ints[3 * dims + 3],    /* psizes */
                                          ints[4 * dims + 3],     /* order */
                                          types[0], &dtype);
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr,
                              "ADIOI_Flatten:: MPI_COMBINER_DARRAY <ADIOI_Flatten(dtype, flat->indices[%#X] %#llX, flat->blocklens[%#X] %#llX, st_offset %#llX, curr_index %#llX);\n",
                              0, flat->indices[0], 0, flat->blocklens[0], st_offset, *curr_index);
  #endif
                  ADIOI_Flatten(dtype, flat, st_offset, curr_index);
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr,
                              "ADIOI_Flatten:: MPI_COMBINER_DARRAY >ADIOI_Flatten(dtype, flat->indices[%#X] %#llX, flat->blocklens[%#X] %#llX, st_offset %#llX, curr_index %#llX);\n",
                              0, flat->indices[0], 0, flat->blocklens[0], st_offset, *curr_index);
  #endif
                  MPI_Type_free(&dtype);
              }
              break;
  #endif
          case MPI_COMBINER_CONTIGUOUS:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_CONTIGUOUS\n");
  #endif
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  ADIOI_Flatten(types[0], flat, st_offset, curr_index);
  
              if (prev_index == *curr_index) {
  /* simplest case, made up of basic or contiguous types */
                  j = *curr_index;
                  flatlist_node_grow(flat, j);
                  flat->indices[j] = st_offset;
                  MPI_Type_size_x(types[0], &old_size);
                  flat->blocklens[j] = top_count * old_size;
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr,
                              "ADIOI_Flatten:: simple flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n",
                              j, flat->indices[j], j, flat->blocklens[j]);
  #endif
                  (*curr_index)++;
              } else {
  /* made up of noncontiguous derived types */
                  j = *curr_index;
                  num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated count times */
                  MPI_Type_extent(types[0], &old_extent);
                  for (m = 1; m < top_count; m++) {
                      for (i = 0; i < num; i++) {
                          flatlist_node_grow(flat, j);
                          flat->indices[j] =
                              flat->indices[j - num] + ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          flat->blocklens[j] = flat->blocklens[j - num];
  #ifdef FLATTEN_DEBUG
                          DBG_FPRINTF(stderr,
                                      "ADIOI_Flatten:: derived flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n",
                                      j, flat->indices[j], j, flat->blocklens[j]);
  #endif
                          j++;
                      }
                  }
                  *curr_index = j;
              }
              break;
  
          case MPI_COMBINER_VECTOR:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_VECTOR\n");
  #endif
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  ADIOI_Flatten(types[0], flat, st_offset, curr_index);
  
              if (prev_index == *curr_index) {
  /* simplest case, vector of basic or contiguous types */
                  /* By using ADIO_Offset we preserve +/- sign and
                   * avoid >2G integer arithmetic problems */
                  ADIO_Offset blocklength = ints[1], stride = ints[2];
                  j = *curr_index;
                  flatlist_node_grow(flat, j);
                  flat->indices[j] = st_offset;
                  MPI_Type_size_x(types[0], &old_size);
                  flat->blocklens[j] = blocklength * old_size;
                  for (i = j + 1; i < j + top_count; i++) {
                      flatlist_node_grow(flat, i);
                      flat->indices[i] = flat->indices[i - 1] + stride * old_size;
                      flat->blocklens[i] = flat->blocklens[j];
                  }
                  *curr_index = i;
              } else {
  /* vector of noncontiguous derived types */
                  /* By using ADIO_Offset we preserve +/- sign and
                   * avoid >2G integer arithmetic problems */
                  ADIO_Offset blocklength = ints[1], stride = ints[2];
  
                  j = *curr_index;
                  num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated blocklen times
     and then strided. Replicate the first one. */
                  MPI_Type_extent(types[0], &old_extent);
                  for (m = 1; m < blocklength; m++) {
                      for (i = 0; i < num; i++) {
                          flatlist_node_grow(flat, j);
                          flat->indices[j] =
                              flat->indices[j - num] + ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          flat->blocklens[j] = flat->blocklens[j - num];
                          j++;
                      }
                  }
                  *curr_index = j;
  
  /* Now repeat with strides. */
                  num = *curr_index - prev_index;
                  for (i = 1; i < top_count; i++) {
                      for (m = 0; m < num; m++) {
                          flatlist_node_grow(flat, j);
                          flat->indices[j] =
                              flat->indices[j - num] + stride * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          flat->blocklens[j] = flat->blocklens[j - num];
                          j++;
                      }
                  }
                  *curr_index = j;
              }
              break;
  
          case MPI_COMBINER_HVECTOR:
          case MPI_COMBINER_HVECTOR_INTEGER:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_HVECTOR_INTEGER\n");
  #endif
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  ADIOI_Flatten(types[0], flat, st_offset, curr_index);
  
              if (prev_index == *curr_index) {
  /* simplest case, vector of basic or contiguous types */
                  /* By using ADIO_Offset we preserve +/- sign and
                   * avoid >2G integer arithmetic problems */
                  ADIO_Offset blocklength = ints[1];
                  j = *curr_index;
                  flatlist_node_grow(flat, j);
                  flat->indices[j] = st_offset;
                  MPI_Type_size_x(types[0], &old_size);
                  flat->blocklens[j] = blocklength * old_size;
                  for (i = j + 1; i < j + top_count; i++) {
                      flatlist_node_grow(flat, i);
                      flat->indices[i] = flat->indices[i - 1] + adds[0];
                      flat->blocklens[i] = flat->blocklens[j];
                  }
                  *curr_index = i;
              } else {
  /* vector of noncontiguous derived types */
                  /* By using ADIO_Offset we preserve +/- sign and
                   * avoid >2G integer arithmetic problems */
                  ADIO_Offset blocklength = ints[1];
  
                  j = *curr_index;
                  num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated blocklen times
     and then strided. Replicate the first one. */
                  MPI_Type_extent(types[0], &old_extent);
                  for (m = 1; m < blocklength; m++) {
                      for (i = 0; i < num; i++) {
                          flatlist_node_grow(flat, j);
                          flat->indices[j] =
                              flat->indices[j - num] + ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          flat->blocklens[j] = flat->blocklens[j - num];
                          j++;
                      }
                  }
                  *curr_index = j;
  
  /* Now repeat with strides. */
                  num = *curr_index - prev_index;
                  for (i = 1; i < top_count; i++) {
                      for (m = 0; m < num; m++) {
                          flatlist_node_grow(flat, j);
                          flat->indices[j] = flat->indices[j - num] + adds[0];
                          flat->blocklens[j] = flat->blocklens[j - num];
                          j++;
                      }
                  }
                  *curr_index = j;
              }
              break;
  
          case MPI_COMBINER_INDEXED:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_INDEXED\n");
  #endif
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
              MPI_Type_extent(types[0], &old_extent);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig)) {
                  /* By using ADIO_Offset we preserve +/- sign and
                   * avoid >2G integer arithmetic problems */
                  ADIO_Offset stride = ints[top_count + 1];
                  ADIOI_Flatten(types[0], flat,
                                st_offset + stride * ADIOI_AINT_CAST_TO_OFFSET old_extent,
                                curr_index);
              }
  
              if (prev_index == *curr_index) {
  /* simplest case, indexed type made up of basic or contiguous types */
                  j = *curr_index;
                  for (i = j, nonzeroth = i; i < j + top_count; i++) {
                      /* By using ADIO_Offset we preserve +/- sign and
                       * avoid >2G integer arithmetic problems */
                      ADIO_Offset blocklength = ints[1 + i - j], stride = ints[top_count + 1 + i - j];
                      if (blocklength > 0) {
                          flatlist_node_grow(flat, nonzeroth);
                          flat->indices[nonzeroth] =
                              st_offset + stride * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          flat->blocklens[nonzeroth] =
                              blocklength * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          nonzeroth++;
                      }
                  }
                  *curr_index = nonzeroth;
              } else {
  /* indexed type made up of noncontiguous derived types */
  
                  j = *curr_index;
                  num = *curr_index - prev_index;
                  basic_num = num;
  
  /* The noncontiguous types have to be replicated blocklens[i] times
     and then strided. Replicate the first one. */
                  for (m = 1; m < ints[1]; m++) {
                      for (i = 0, nonzeroth = j; i < num; i++) {
                          if (flat->blocklens[j - num] > 0) {
                              flatlist_node_grow(flat, nonzeroth);
                              flat->indices[nonzeroth] =
                                  flat->indices[nonzeroth - num] +
                                  ADIOI_AINT_CAST_TO_OFFSET old_extent;
                              flat->blocklens[nonzeroth] = flat->blocklens[nonzeroth - num];
                              j++;
                              nonzeroth++;
                          }
                      }
                  }
                  *curr_index = j;
  
  /* Now repeat with strides. */
                  for (i = 1; i < top_count; i++) {
                      num = *curr_index - prev_index;
                      prev_index = *curr_index;
                      for (m = 0, nonzeroth = j; m < basic_num; m++) {
                          /* By using ADIO_Offset we preserve +/- sign and
                           * avoid >2G integer arithmetic problems */
                          ADIO_Offset stride = ints[top_count + 1 + i] - ints[top_count + i];
                          if (flat->blocklens[j - num] > 0) {
                              flatlist_node_grow(flat, nonzeroth);
                              flat->indices[nonzeroth] =
                                  flat->indices[j - num] +
                                  stride * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                              flat->blocklens[nonzeroth] = flat->blocklens[j - num];
                              j++;
                              nonzeroth++;
                          }
                      }
                      *curr_index = j;
                      for (m = 1; m < ints[1 + i]; m++) {
                          for (k = 0, nonzeroth = j; k < basic_num; k++) {
                              if (flat->blocklens[j - basic_num] > 0) {
                                  flatlist_node_grow(flat, nonzeroth);
                                  flat->indices[nonzeroth] =
                                      flat->indices[j - basic_num] +
                                      ADIOI_AINT_CAST_TO_OFFSET old_extent;
                                  flat->blocklens[nonzeroth] = flat->blocklens[j - basic_num];
                                  j++;
                                  nonzeroth++;
                              }
                          }
                      }
                      *curr_index = j;
                  }
              }
              break;
  
  #if defined HAVE_DECL_MPI_COMBINER_HINDEXED_BLOCK && HAVE_DECL_MPI_COMBINER_HINDEXED_BLOCK
          case MPI_COMBINER_HINDEXED_BLOCK:
              is_hindexed_block = 1;
              /* deliberate fall-through */
              MPL_FALLTHROUGH;
  #endif
          case MPI_COMBINER_INDEXED_BLOCK:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_INDEXED_BLOCK\n");
  #endif
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
              MPI_Type_extent(types[0], &old_extent);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig)) {
                  /* By using ADIO_Offset we preserve +/- sign and
                   * avoid >2G integer arithmetic problems */
                  ADIO_Offset stride = ints[1 + 1];
                  if (is_hindexed_block) {
                      ADIOI_Flatten(types[0], flat, st_offset + adds[0], curr_index);
                  } else {
                      ADIOI_Flatten(types[0], flat,
                                    st_offset + stride * ADIOI_AINT_CAST_TO_OFFSET old_extent,
                                    curr_index);
                  }
              }
  
              if (prev_index == *curr_index) {
  /* simplest case, indexed type made up of basic or contiguous types */
                  j = *curr_index;
                  for (i = j; i < j + top_count; i++) {
                      /* By using ADIO_Offset we preserve +/- sign and
                       * avoid >2G integer arithmetic problems */
                      ADIO_Offset blocklength = ints[1];
                      if (is_hindexed_block) {
                          flatlist_node_grow(flat, i);
                          flat->indices[i] = st_offset + adds[i - j];
                      } else {
                          ADIO_Offset stride = ints[1 + 1 + i - j];
                          flatlist_node_grow(flat, i);
                          flat->indices[i] = st_offset +
                              stride * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                      }
                      flat->blocklens[i] = blocklength * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                  }
                  *curr_index = i;
              } else {
  /* vector of noncontiguous derived types */
  
                  j = *curr_index;
                  num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated blocklens[i] times
     and then strided. Replicate the first one. */
                  for (m = 1; m < ints[1]; m++) {
                      for (i = 0; i < num; i++) {
                          if (is_hindexed_block) {
                              /* this is the one place the hindexed case uses the
                               * extent of a type */
                              MPI_Type_extent(types[0], &old_extent);
                          }
                          flatlist_node_grow(flat, j);
                          flat->indices[j] = flat->indices[j - num] +
                              ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          flat->blocklens[j] = flat->blocklens[j - num];
                          j++;
                      }
                  }
                  *curr_index = j;
  
  /* Now repeat with strides. */
                  num = *curr_index - prev_index;
                  for (i = 1; i < top_count; i++) {
                      for (m = 0; m < num; m++) {
                          if (is_hindexed_block) {
                              flatlist_node_grow(flat, j);
                              flat->indices[j] = flat->indices[j - num] + adds[i] - adds[i - 1];
                          } else {
                              /* By using ADIO_Offset we preserve +/- sign and
                               * avoid >2G integer arithmetic problems */
                              ADIO_Offset stride = ints[2 + i] - ints[1 + i];
                              flatlist_node_grow(flat, j);
                              flat->indices[j] = flat->indices[j - num] +
                                  stride * ADIOI_AINT_CAST_TO_OFFSET old_extent;
                          }
                          flat->blocklens[j] = flat->blocklens[j - num];
                          j++;
                      }
                  }
                  *curr_index = j;
              }
              break;
  
          case MPI_COMBINER_HINDEXED:
          case MPI_COMBINER_HINDEXED_INTEGER:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_HINDEXED_INTEGER\n");
  #endif
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig)) {
                  ADIOI_Flatten(types[0], flat, st_offset + adds[0], curr_index);
              }
  
              if (prev_index == *curr_index) {
  /* simplest case, indexed type made up of basic or contiguous types */
                  j = *curr_index;
                  MPI_Type_size_x(types[0], &old_size);
                  for (i = j, nonzeroth = j; i < j + top_count; i++) {
                      if (ints[1 + i - j] > 0) {
                          /* By using ADIO_Offset we preserve +/- sign and
                           * avoid >2G integer arithmetic problems */
                          ADIO_Offset blocklength = ints[1 + i - j];
                          flatlist_node_grow(flat, nonzeroth);
                          flat->indices[nonzeroth] = st_offset + adds[i - j];
                          flat->blocklens[nonzeroth] = blocklength * old_size;
                          nonzeroth++;
                      }
                  }
                  *curr_index = nonzeroth;
              } else {
  /* indexed type made up of noncontiguous derived types */
  
                  j = *curr_index;
                  num = *curr_index - prev_index;
                  basic_num = num;
  
  /* The noncontiguous types have to be replicated blocklens[i] times
     and then strided. Replicate the first one. */
                  MPI_Type_extent(types[0], &old_extent);
                  for (m = 1; m < ints[1]; m++) {
                      for (i = 0, nonzeroth = j; i < num; i++) {
                          if (flat->blocklens[j - num] > 0) {
                              flatlist_node_grow(flat, nonzeroth);
                              flat->indices[nonzeroth] =
                                  flat->indices[j - num] + ADIOI_AINT_CAST_TO_OFFSET old_extent;
                              flat->blocklens[nonzeroth] = flat->blocklens[j - num];
                              j++;
                              nonzeroth++;
                          }
                      }
                  }
                  *curr_index = j;
  
  /* Now repeat with strides. */
                  for (i = 1; i < top_count; i++) {
                      num = *curr_index - prev_index;
                      prev_index = *curr_index;
                      for (m = 0, nonzeroth = j; m < basic_num; m++) {
                          if (flat->blocklens[j - num] > 0) {
                              flatlist_node_grow(flat, nonzeroth);
                              flat->indices[nonzeroth] =
                                  flat->indices[j - num] + adds[i] - adds[i - 1];
                              flat->blocklens[nonzeroth] = flat->blocklens[j - num];
                              j++;
                              nonzeroth++;
                          }
                      }
                      *curr_index = j;
                      for (m = 1; m < ints[1 + i]; m++) {
                          for (k = 0, nonzeroth = j; k < basic_num; k++) {
                              if (flat->blocklens[j - basic_num] > 0) {
                                  flatlist_node_grow(flat, nonzeroth);
                                  flat->indices[nonzeroth] =
                                      flat->indices[j - basic_num] +
                                      ADIOI_AINT_CAST_TO_OFFSET old_extent;
                                  flat->blocklens[nonzeroth] = flat->blocklens[j - basic_num];
                                  j++;
                                  nonzeroth++;
                              }
                          }
                      }
                      *curr_index = j;
                  }
              }
              break;
  
          case MPI_COMBINER_STRUCT:
          case MPI_COMBINER_STRUCT_INTEGER:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_STRUCT_INTEGER\n");
  #endif
              top_count = ints[0];
              for (n = 0; n < top_count; n++) {
                  MPI_Type_get_envelope(types[n], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
                  ADIOI_Datatype_iscontig(types[n], &old_is_contig);
  
                  prev_index = *curr_index;
                  if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                      ADIOI_Flatten(types[n], flat, st_offset + adds[n], curr_index);
  
                  if (prev_index == *curr_index) {
  /* simplest case, current type is basic or contiguous types */
                      /* By using ADIO_Offset we preserve +/- sign and
                       * avoid >2G integer arithmetic problems */
                      if (ints[1 + n] > 0 || types[n] == MPI_LB || types[n] == MPI_UB) {
                          ADIO_Offset blocklength = ints[1 + n];
                          j = *curr_index;
                          flatlist_node_grow(flat, j);
                          flat->indices[j] = st_offset + adds[n];
                          MPI_Type_size_x(types[n], &old_size);
                          flat->blocklens[j] = blocklength * old_size;
                          if (types[n] == MPI_LB)
                              flat->lb_idx = j;
                          if (types[n] == MPI_UB)
                              flat->ub_idx = j;
  #ifdef FLATTEN_DEBUG
                          DBG_FPRINTF(stderr,
                                      "ADIOI_Flatten:: simple adds[%#X] " MPI_AINT_FMT_HEX_SPEC
                                      ", flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n",
                                      n, adds[n], j, flat->indices[j], j, flat->blocklens[j]);
  #endif
                          (*curr_index)++;
                      }
                  } else {
  /* current type made up of noncontiguous derived types */
  
                      j = *curr_index;
                      num = *curr_index - prev_index;
  
  /* The current type has to be replicated blocklens[n] times */
                      MPI_Type_extent(types[n], &old_extent);
                      for (m = 1; m < ints[1 + n]; m++) {
                          for (i = 0; i < num; i++) {
                              flatlist_node_grow(flat, j);
                              flat->indices[j] =
                                  flat->indices[j - num] + ADIOI_AINT_CAST_TO_OFFSET old_extent;
                              flat->blocklens[j] = flat->blocklens[j - num];
  #ifdef FLATTEN_DEBUG
                              DBG_FPRINTF(stderr,
                                          "ADIOI_Flatten:: simple old_extent " MPI_AINT_FMT_HEX_SPEC
                                          ", flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n",
                                          old_extent, j, flat->indices[j], j, flat->blocklens[j]);
  #endif
                              j++;
                          }
                      }
                      *curr_index = j;
                  }
              }
              break;
  
          case MPI_COMBINER_RESIZED:
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr, "ADIOI_Flatten:: MPI_COMBINER_RESIZED\n");
  #endif
  
              /* This is done similar to a type_struct with an lb, datatype, ub */
  
              /* handle the Lb */
              j = *curr_index;
              /* when we process resized types, we (recursively) process the lower
               * bound, the type being resized, then the upper bound.  In the
               * resized-of-resized case, we might find ourselves updating the upper
               * bound based on the inner type, but the lower bound based on the
               * upper type.  check both lb and ub to prevent mixing updates */
              if (flat->lb_idx == -1 && flat->ub_idx == -1) {
                  flatlist_node_grow(flat, j);
                  flat->indices[j] = st_offset + adds[0];
                  /* this zero-length blocklens[] element, unlike elsewhere in the
                   * flattening code, is correct and is used to indicate a lower bound
                   * marker */
                  flat->blocklens[j] = 0;
                  flat->lb_idx = *curr_index;
                  lb_updated = 1;
  
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr,
                              "ADIOI_Flatten:: simple adds[%#X] " MPI_AINT_FMT_HEX_SPEC
                              ", flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n", 0,
                              adds[0], j, flat->indices[j], j, flat->blocklens[j]);
  #endif
  
                  (*curr_index)++;
              } else {
                  /* skipped over this chunk because something else higher-up in the
                   * type construction set this for us already */
                  st_offset -= adds[0];
              }
  
              /* handle the datatype */
  
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig)) {
                  ADIOI_Flatten(types[0], flat, st_offset + adds[0], curr_index);
              } else {
                  /* current type is basic or contiguous */
                  j = *curr_index;
                  flatlist_node_grow(flat, j);
                  flat->indices[j] = st_offset;
                  MPI_Type_size_x(types[0], &old_size);
                  flat->blocklens[j] = old_size;
  
  #ifdef FLATTEN_DEBUG
                  DBG_FPRINTF(stderr,
                              "ADIOI_Flatten:: simple adds[%#X] " MPI_AINT_FMT_HEX_SPEC
                              ", flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n", 0,
                              adds[0], j, flat->indices[j], j, flat->blocklens[j]);
  #endif
  
                  (*curr_index)++;
              }
  
              /* take care of the extent as a UB */
              /* see note above about mixing updates for why we check lb and ub */
              if ((flat->lb_idx == -1 && flat->ub_idx == -1) || lb_updated) {
                  j = *curr_index;
                  flatlist_node_grow(flat, j);
                  flat->indices[j] = st_offset + adds[0] + adds[1];
                  /* again, zero-element ok: an upper-bound marker explicitly set by the
                   * constructor of this resized type */
                  flat->blocklens[j] = 0;
                  flat->ub_idx = *curr_index;
              } else {
                  /* skipped over this chunk because something else higher-up in the
                   * type construction set this for us already */
                  (*curr_index)--;
              }
  
  #ifdef FLATTEN_DEBUG
              DBG_FPRINTF(stderr,
                          "ADIOI_Flatten:: simple adds[%#X] " MPI_AINT_FMT_HEX_SPEC
                          ", flat->indices[%#llX] %#llX, flat->blocklens[%#llX] %#llX\n", 1, adds[1],
                          j, flat->indices[j], j, flat->blocklens[j]);
  #endif
  
              (*curr_index)++;
  
              break;
  
          default:
              /* TODO: FIXME (requires changing prototypes to return errors...) */
              DBG_FPRINTF(stderr, "Error: Unsupported datatype passed to ADIOI_Flatten\n");
              MPI_Abort(MPI_COMM_WORLD, 1);
      }
  
      for (i = 0; i < ntypes; i++) {
          MPI_Type_get_envelope(types[i], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
          if (old_combiner != MPI_COMBINER_NAMED)
              MPI_Type_free(types + i);
      }
  
      ADIOI_Free(ints);
      ADIOI_Free(adds);
      ADIOI_Free(types);
  
  #ifdef FLATTEN_DEBUG
      DBG_FPRINTF(stderr, "ADIOI_Flatten:: return st_offset %#llX, curr_index %#llX\n", st_offset,
                  *curr_index);
  #endif
  
  }
  
  /********************************************************/
  
  /* ADIOI_Count_contiguous_blocks
   *
   * Returns number of contiguous blocks in type, and also updates
   * curr_index to reflect the space for the additional blocks.
   *
   * ASSUMES THAT TYPE IS NOT A BASIC!!!
   */
  MPI_Count ADIOI_Count_contiguous_blocks(MPI_Datatype datatype, MPI_Count * curr_index)
  {
      int i, n;
      MPI_Count count = 0, prev_index, num, basic_num;
      int top_count, combiner, old_combiner, old_is_contig;
      int nints, nadds, ntypes, old_nints, old_nadds, old_ntypes;
      int *ints;
      MPI_Aint *adds;             /* Make no assumptions about +/- sign on these */
      MPI_Datatype *types;
  
      MPI_Type_get_envelope(datatype, &nints, &nadds, &ntypes, &combiner);
      ints = (int *) ADIOI_Malloc((nints + 1) * sizeof(int));
      adds = (MPI_Aint *) ADIOI_Malloc((nadds + 1) * sizeof(MPI_Aint));
      types = (MPI_Datatype *) ADIOI_Malloc((ntypes + 1) * sizeof(MPI_Datatype));
      MPI_Type_get_contents(datatype, nints, nadds, ntypes, ints, adds, types);
  
      switch (combiner) {
  #ifdef MPIIMPL_HAVE_MPI_COMBINER_DUP
          case MPI_COMBINER_DUP:
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  count = ADIOI_Count_contiguous_blocks(types[0], curr_index);
              else {
                  count = 1;
                  (*curr_index)++;
              }
              break;
  #endif
  #ifdef MPIIMPL_HAVE_MPI_COMBINER_SUBARRAY
          case MPI_COMBINER_SUBARRAY:
              {
                  int dims = ints[0];
                  MPI_Datatype stype;
  
                  ADIO_Type_create_subarray(dims, &ints[1],       /* sizes */
                                            &ints[dims + 1],      /* subsizes */
                                            &ints[2 * dims + 1],  /* starts */
                                            ints[3 * dims + 1],   /* order */
                                            types[0],     /* type */
                                            &stype);
                  count = ADIOI_Count_contiguous_blocks(stype, curr_index);
                  /* curr_index will have already been updated; just pass
                   * count back up.
                   */
                  MPI_Type_free(&stype);
  
              }
              break;
  #endif
  #ifdef MPIIMPL_HAVE_MPI_COMBINER_DARRAY
          case MPI_COMBINER_DARRAY:
              {
                  int dims = ints[2];
                  MPI_Datatype dtype;
  
                  ADIO_Type_create_darray(ints[0],        /* size */
                                          ints[1],        /* rank */
                                          dims, &ints[3], /* gsizes */
                                          &ints[dims + 3],        /* distribs */
                                          &ints[2 * dims + 3],    /* dargs */
                                          &ints[3 * dims + 3],    /* psizes */
                                          ints[4 * dims + 3],     /* order */
                                          types[0], &dtype);
                  count = ADIOI_Count_contiguous_blocks(dtype, curr_index);
                  /* curr_index will have already been updated; just pass
                   * count back up.
                   */
                  MPI_Type_free(&dtype);
              }
              break;
  #endif
          case MPI_COMBINER_CONTIGUOUS:
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  count = ADIOI_Count_contiguous_blocks(types[0], curr_index);
              else
                  count = 1;
  
              if (prev_index == *curr_index)
  /* simplest case, made up of basic or contiguous types */
                  (*curr_index)++;
              else {
  /* made up of noncontiguous derived types */
                  num = *curr_index - prev_index;
                  count *= top_count;
                  *curr_index += (top_count - 1) * num;
              }
              break;
  
          case MPI_COMBINER_VECTOR:
          case MPI_COMBINER_HVECTOR:
          case MPI_COMBINER_HVECTOR_INTEGER:
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  count = ADIOI_Count_contiguous_blocks(types[0], curr_index);
              else
                  count = 1;
  
              if (prev_index == *curr_index) {
  /* simplest case, vector of basic or contiguous types */
                  count = top_count;
                  *curr_index += count;
              } else {
  /* vector of noncontiguous derived types */
                  num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated blocklen times
     and then strided. */
                  count *= ints[1] * top_count;
  
  /* First one */
                  *curr_index += (ints[1] - 1) * num;
  
  /* Now repeat with strides. */
                  num = *curr_index - prev_index;
                  *curr_index += (top_count - 1) * num;
              }
              break;
  
          case MPI_COMBINER_INDEXED:
          case MPI_COMBINER_HINDEXED:
          case MPI_COMBINER_HINDEXED_INTEGER:
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  count = ADIOI_Count_contiguous_blocks(types[0], curr_index);
              else
                  count = 1;
  
              if (prev_index == *curr_index) {
  /* simplest case, indexed type made up of basic or contiguous types */
                  count = top_count;
                  *curr_index += count;
              } else {
  /* indexed type made up of noncontiguous derived types */
                  basic_num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated blocklens[i] times
     and then strided. */
                  *curr_index += (ints[1] - 1) * basic_num;
                  count *= ints[1];
  
  /* Now repeat with strides. */
                  for (i = 1; i < top_count; i++) {
                      count += ints[1 + i] * basic_num;
                      *curr_index += ints[1 + i] * basic_num;
                  }
              }
              break;
  
  #if defined HAVE_DECL_MPI_COMBINER_HINDEXED_BLOCK && HAVE_DECL_MPI_COMBINER_HINDEXED_BLOCK
          case MPI_COMBINER_HINDEXED_BLOCK:
  #endif
          case MPI_COMBINER_INDEXED_BLOCK:
              top_count = ints[0];
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              prev_index = *curr_index;
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                  count = ADIOI_Count_contiguous_blocks(types[0], curr_index);
              else
                  count = 1;
  
              if (prev_index == *curr_index) {
  /* simplest case, indexed type made up of basic or contiguous types */
                  count = top_count;
                  *curr_index += count;
              } else {
  /* indexed type made up of noncontiguous derived types */
                  basic_num = *curr_index - prev_index;
  
  /* The noncontiguous types have to be replicated blocklens[i] times
     and then strided. */
                  *curr_index += (ints[1] - 1) * basic_num;
                  count *= ints[1];
  
  /* Now repeat with strides. */
                  *curr_index += (top_count - 1) * count;
                  count *= top_count;
              }
              break;
  
          case MPI_COMBINER_STRUCT:
          case MPI_COMBINER_STRUCT_INTEGER:
              top_count = ints[0];
              count = 0;
              for (n = 0; n < top_count; n++) {
                  MPI_Type_get_envelope(types[n], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
                  ADIOI_Datatype_iscontig(types[n], &old_is_contig);
  
                  prev_index = *curr_index;
                  if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig))
                      count += ADIOI_Count_contiguous_blocks(types[n], curr_index);
  
                  if (prev_index == *curr_index) {
  /* simplest case, current type is basic or contiguous types */
                      count++;
                      (*curr_index)++;
                  } else {
  /* current type made up of noncontiguous derived types */
  /* The current type has to be replicated blocklens[n] times */
  
                      num = *curr_index - prev_index;
                      count += (ints[1 + n] - 1) * num;
                      (*curr_index) += (ints[1 + n] - 1) * num;
                  }
              }
              break;
  
          case MPI_COMBINER_RESIZED:
              /* treat it as a struct with lb, type, ub */
  
              /* add 2 for lb and ub */
              (*curr_index) += 2;
              count += 2;
  
              /* add for datatype */
              MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
              ADIOI_Datatype_iscontig(types[0], &old_is_contig);
  
              if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig)) {
                  count += ADIOI_Count_contiguous_blocks(types[0], curr_index);
              } else {
                  /* basic or contiguous type */
                  count++;
                  (*curr_index)++;
              }
              break;
  
          default:
              /* TODO: FIXME */
              DBG_FPRINTF(stderr,
                          "Error: Unsupported datatype passed to ADIOI_Count_contiguous_blocks, combiner = %d\n",
                          combiner);
              MPI_Abort(MPI_COMM_WORLD, 1);
      }
  
      for (i = 0; i < ntypes; i++) {
          MPI_Type_get_envelope(types[i], &old_nints, &old_nadds, &old_ntypes, &old_combiner);
          if (old_combiner != MPI_COMBINER_NAMED)
              MPI_Type_free(types + i);
      }
  
      ADIOI_Free(ints);
      ADIOI_Free(adds);
      ADIOI_Free(types);
      return count;
  }
  
  
  /****************************************************************/
  
  /* ADIOI_Optimize_flattened()
   *
   * Scans the blocks of a flattened type and merges adjacent blocks
   * together, resulting in a shorter blocklist (and thus fewer
   * contiguous operations).
   *
   * NOTE: a further optimization would be to remove zero length blocks. However,
   * the first and last blocks must remain as zero length first or last block
   * indicates UB and LB.  Furthermore, once the "zero length blocklen" fix
   * went in, the flattened representation should no longer have zero-length
   * blocks except for UB and LB markers.
   */
  void ADIOI_Optimize_flattened(ADIOI_Flatlist_node * flat_type)
  {
      int i, j, opt_blocks;
      ADIO_Offset *opt_blocklens;
      ADIO_Offset *opt_indices;
  
      opt_blocks = 1;
  
      for (j = -1, i = 0; i < flat_type->count; i++) {
          /* save number of noncontiguous blocks in opt_blocks */
          if (i < flat_type->count - 1 &&
              (flat_type->indices[i] + flat_type->blocklens[i] != flat_type->indices[i + 1]))
              opt_blocks++;
  
          /* Check if any of the displacements is negative */
          if (flat_type->blocklens[i] > 0 && flat_type->indices[i] < 0)
              flat_type->flag |= ADIOI_TYPE_NEGATIVE;
  
          if (flat_type->blocklens[i] == 0)       /* skip zero-length block */
              continue;
          else if (j == -1) {
              j = i;      /* set j the first non-zero-length block index */
              continue;
          }
  
          /* Check if displacements are in a monotonic nondecreasing order */
          if (flat_type->indices[j] > flat_type->indices[i])
              flat_type->flag |= ADIOI_TYPE_DECREASE;
  
          /* Check for overlapping regions */
          if (flat_type->indices[j] + flat_type->blocklens[j] > flat_type->indices[i])
              flat_type->flag |= ADIOI_TYPE_OVERLAP;
  
          j = i;  /* j is the previous non-zero-length block index */
      }
  
      /* if we can't reduce the number of blocks, quit now */
      if (opt_blocks == flat_type->count)
          return;
  
      opt_blocklens = (ADIO_Offset *) ADIOI_Calloc(opt_blocks * 2, sizeof(ADIO_Offset));
      opt_indices = opt_blocklens + opt_blocks;
  
      /* fill in new blocklists */
      opt_blocklens[0] = flat_type->blocklens[0];
      opt_indices[0] = flat_type->indices[0];
      j = 0;
      for (i = 0; i < (flat_type->count - 1); i++) {
          if ((flat_type->indices[i] + flat_type->blocklens[i] == flat_type->indices[i + 1]))
              opt_blocklens[j] += flat_type->blocklens[i + 1];
          else {
              j++;
              opt_indices[j] = flat_type->indices[i + 1];
              opt_blocklens[j] = flat_type->blocklens[i + 1];
          }
      }
      flat_type->count = opt_blocks;
      ADIOI_Free(flat_type->blocklens);
      flat_type->blocklens = opt_blocklens;
      flat_type->indices = opt_indices;
      return;
  }
  
  int ADIOI_Flattened_type_keyval = MPI_KEYVAL_INVALID;
  
  int ADIOI_Flattened_type_copy(MPI_Datatype oldtype,
                                int type_keyval, void *extra_state, void *attribute_val_in,
                                void *attribute_val_out, int *flag)
  {
      ADIOI_Flatlist_node *node = (ADIOI_Flatlist_node *) attribute_val_in;
      if (node != NULL)
          node->refct++;
      *(ADIOI_Flatlist_node **) attribute_val_out = node;
      *flag = 1;  /* attribute copied to new communicator */
      return MPI_SUCCESS;
  }
  
  int ADIOI_Flattened_type_delete(MPI_Datatype datatype,
                                  int type_keyval, void *attribute_val, void *extra_state)
  {
      ADIOI_Flatlist_node *node = (ADIOI_Flatlist_node *) attribute_val;
      ADIOI_Assert(node != NULL);
      node->refct--;
  
      if (node->refct <= 0) {
          ADIOI_Free(node->blocklens);
          ADIOI_Free(node);
      }
  
      return MPI_SUCCESS;
  }
  
  ADIOI_Flatlist_node *ADIOI_Flatten_and_find(MPI_Datatype datatype)
  {
      ADIOI_Flatlist_node *node;
      int flag = 0;
  
      if (ADIOI_Flattened_type_keyval == MPI_KEYVAL_INVALID) {
          /* ADIOI_End_call will take care of cleanup */
          MPI_Type_create_keyval(ADIOI_Flattened_type_copy,
                                 ADIOI_Flattened_type_delete, &ADIOI_Flattened_type_keyval, NULL);
      }
  
      MPI_Type_get_attr(datatype, ADIOI_Flattened_type_keyval, &node, &flag);
      if (flag == 0) {
          node = ADIOI_Flatten_datatype(datatype);
      }
  
      return node;
  }
  