//==============================================================================
int AztecDVBR_Matrix::getBlockSize(int blkRow, int blkCol,
                                   int& ptRows, int& ptCols) {
   int index;

   ptRows = 0;
   ptCols = 0;

   if (!inUpdate(blkRow, index)) {
      fei::console_out() << "AztecDVBR_Matrix::getBlockSize: ERROR: blkRow "
           << blkRow << " not in local update list." << FEI_ENDL;
      return(1);
   }

   ptRows = Amat_->rpntr[index+1] - Amat_->rpntr[index];

   int local = inUpdate(blkCol, index);

   if (local) {
      ptCols = Amat_->rpntr[index+1] - Amat_->rpntr[index];
   }
   else {
      index = AZ_find_index(blkCol, remoteInds_, numRemoteBlocks_);

      if (index < 0) return(1);
      else ptCols = remoteBlockSizes_[index];
   }

   return(0);
}
//==============================================================================
void AztecDVBR_Matrix::readMatrixData(FILE* infile) {

   int blkRows, blkCols, rows, cols;
   int br, bc, pr, pc, nnz, index;
   double* blockValues = NULL;
   char line[256];

   do {
      fgets(line,256,infile);
   } while(strchr(line,'%'));
   sscanf(line,"%d %d %d %d",&blkRows, &blkCols, &rows, &cols);

   while (!feof(infile))  {
      do {
         fgets(line,256,infile);
      } while(strchr(line,'%'));

      if(feof(infile))break;

      sscanf(line, "%d %d %d %d %d", &br, &bc, &pr, &pc, &nnz);

      if (inUpdate(br, index)){
         //br (block-row) is in the local row-space, so let's
         //plug this block into our matrix data structure.

         blockValues = new double[nnz];
         getValuesFromString(line, std::strlen(line)+1, blockValues, nnz);

         putBlockRow(br, blockValues, &bc, 1);

         delete [] blockValues;
      }
   }
}
//==============================================================================
int AztecDVBR_Matrix::getBlockRow(int blkRow,
                                  double* val,
                                  int* blkColInds,
                                  int numNzBlks) const {

   if (!isAllocated()) return(1);

   int index;

   if (!inUpdate(blkRow, index)) {
      fei::console_out() << "AztecDVBR_Matrix::getBlockRow: ERROR: blkRow "
           << blkRow << " not in local update list." << FEI_ENDL;
      return(1);
   }

   //for each block, we need to find its block column index
   //in the bindx array, then go to that same position in the indx
   //array to find out how many point-entries are in that block.
   //We can then use the indx entry to go to the val array and get
   //the data.

   int nnzBlks = 0, nnzPts = 0;
   int err = getNumBlocksPerRow(blkRow, nnzBlks);
   if (err) return(err);
   err = getNumNonzerosPerRow(blkRow, nnzPts);
   if (err) return(err);
   
   if (numNzBlks != nnzBlks) return(1);

   int offset = 0;
   int blkCounter = 0;
   const int* blkUpdate = amap_->getBlockUpdate();
   for(int indb = Amat_->bpntr[index]; indb<Amat_->bpntr[index+1]; indb++) {

      int numEntries = Amat_->indx[indb+1] - Amat_->indx[indb];
      int valOffset = Amat_->indx[indb];

      if (isLoaded()) {
         int ind = Amat_->bindx[indb];
         if (ind < N_update_) {
            blkColInds[blkCounter++] = blkUpdate[orderingUpdate_[ind]];
         }
         else {
            blkColInds[blkCounter++] = external_[ind-N_update_];
         }
      }
      else {
         blkColInds[blkCounter++] = Amat_->bindx[indb];
      }

      //ok, now we're ready to get the stuff.
      for(int i=0; i<numEntries; i++) {
         val[offset + i] = Amat_->val[valOffset + i];
      }

      offset += numEntries;
   }

   return(0);
}
//==============================================================================
int AztecDVBR_Matrix::sumIntoBlockRow(int blkRow,
                                       double* val,
                                       int* blkColInds,
                                       int numNzBlks) const
{
  //
  //This function is the same as putBlockRow, except the values
  //are summed into any existing values rather than overwriting
  //them.
  //
  if (!isAllocated()) return(1);

  int index;

  if (!inUpdate(blkRow, index)) {
    fei::console_out() << "AztecDVBR_Matrix::sumIntoBlockRow: ERROR: blkRow "
	 << blkRow << " not in local update list." << FEI_ENDL;
    return(1);
  }

  //for each incoming block, we need to find its block column index
  //in the bindx array, then go to that same position in the indx
  //array to find out how many (point) entries are in that block.
  //We can then use the indx entry to go to the val array and store
  //the data.

  int offset = 0;

  for(int blk = 0; blk<numNzBlks; blk++) {
    int indb = getBindxOffset(blkColInds[blk],
			      Amat_->bpntr[index], Amat_->bpntr[index+1]-1);

    if (indb < 0) {
      fei::console_out() << "AztecDVBR_Matrix::sumIntoBlockRow: blk col "
	   << blkColInds[blk] << " not found in row " << blkRow << FEI_ENDL;
      abort();
    }

    int numEntries = Amat_->indx[indb+1] - Amat_->indx[indb];
    int valOffset = Amat_->indx[indb];

    //ok, now we're ready to store the stuff.
    for(int i=0; i<numEntries; i++) {
      Amat_->val[valOffset + i] += val[offset + i];
    }

    offset += numEntries;
  }

  return(0);
}
//==============================================================================
void AztecDVBR_Matrix::calcRemoteInds(int*& remoteInds, int& len) {
//
//Form a list of the block column indices that are not in the local
//update set.
//
   int nnzBlks = Amat_->bpntr[amap_->getNumLocalBlocks()];
   int local;

   for(int i=0; i<nnzBlks; i++) {
      if (!inUpdate(Amat_->bindx[i], local)) {
         insertList(Amat_->bindx[i], remoteInds, len);
      }
   }
}
//==============================================================================
int AztecDVBR_Matrix::getNumBlocksPerRow(int blkRow, int& nnzBlksPerRow) const {
//
//On return, nnzBlksPerRow will be the number of nonzero blocks in row blkRow.
//
   if (!isAllocated()) return(1);

   int index;

   if (!inUpdate(blkRow, index)) {
      fei::console_out() << "AztecDVBR_Matrix::getNumBlocksPerRow: ERROR: blkRow "
           << blkRow << " not in local update list." << FEI_ENDL;
      return(1);
   }

   nnzBlksPerRow = Amat_->bpntr[index+1] - Amat_->bpntr[index];

   return(0);
}
//==============================================================================
int AztecDVBR_Matrix::getNumNonzerosPerRow(int blkRow, int& nnzPerRow) const {
//
//This function finds nnzPerRow, the number of nonzero *point* entries for
//row 'blkRow'.
//
   if (!isAllocated()) return(1);

   int index;

   if (!inUpdate(blkRow, index)) {
      fei::console_out() << "AztecDVBR_Matrix::getNumNonzerosPerRow: ERROR: blkRow "
           << blkRow << " not in local update list." << FEI_ENDL;
      return(1);
   }

   nnzPerRow = nnzPerRow_[index];

   return(0);
}
//==============================================================================
void AztecDVBR_Matrix::calcIndx(int nnzBlks) {
//
//This function allocates and fills the Amat_->indx array, which holds info
//on the number of entries in each nonzero block.
//
//indx[0..bpntr[M]], (where M = number of local block rows)
//indx[0] = 0
//indx[k+1]-indx[k] = number of entries in nonzero block k
//

   Amat_->indx = new int[nnzBlks+1];

   //we need to obtain block sizes for all local nonzero blocks. rpntr
   //gives us the sizes for the blocks with column indices in the local
   //update set, but we'll have to do some message passing to obtain the
   //sizes of blocks with column indices in other procs' update sets.

   int numProcs = amap_->getProcConfig()[AZ_N_procs];

   if (numProcs > 1) {
      //form a list of the column indices that are not local.
      calcRemoteInds(remoteInds_, numRemoteBlocks_);

      //now get sizes of blocks that correspond to remote rows.
      remoteBlockSizes_ = new int[numRemoteBlocks_];
      getRemoteBlkSizes(remoteBlockSizes_, remoteInds_, numRemoteBlocks_);
   }

   //now we're ready to set the block sizes in Amat_->indx.
   int index;

   Amat_->indx[0] = 0;

   for(int i=0; i<amap_->getNumLocalBlocks(); i++) {
      int rowBlkSize = Amat_->rpntr[i+1] - Amat_->rpntr[i];

      int colStart = Amat_->bpntr[i];
      int colEnd = Amat_->bpntr[i+1] - 1;

      for(int j=colStart; j<=colEnd; j++) {
         if (inUpdate(Amat_->bindx[j], index)) {
            int colBlkSize = Amat_->rpntr[index+1] - Amat_->rpntr[index];

            Amat_->indx[j+1] = Amat_->indx[j] + rowBlkSize*colBlkSize;
         }
         else { //it's a remoteIndex
            if (numProcs == 1) {
               char mesg[80];
               sprintf(mesg,"calcIndx: blk col index %d not in update set.",
                       Amat_->bindx[j]);
               messageAbort(mesg);
            }

            index = AZ_find_index(Amat_->bindx[j], remoteInds_,
                                  numRemoteBlocks_);
            if (index >= 0) {
               Amat_->indx[j+1] = Amat_->indx[j] +  
                                rowBlkSize*remoteBlockSizes_[index];
            }
            else { //if it wasn't in update or remoteInds, then panic!
               messageAbort("calcIndx: block column index not found.");
            }
         }
      } // end for j loop

      nnzPerRow_[i] = Amat_->indx[colEnd+1] - Amat_->indx[colStart];
   } // end for i loop

   localNNZ_ = Amat_->indx[nnzBlks];
}
//==============================================================================
void AztecDVBR_Matrix::readAllocateInfo(FILE* infile,
                                     int*& num_nz_blocks,
                                     int*& blk_col_inds) {
//
//This function will read through infile and construct the lists
//num_nz_blocks (which is the number of nonzero blocks per row) and
//blk_col_inds (which is the block-column indices of those blocks).
//
//It is assumed that these two lists are empty when this function is
//called.

   int i;

   if (num_nz_blocks) delete [] num_nz_blocks;
   if (blk_col_inds) delete [] blk_col_inds;

   num_nz_blocks = new int[N_update_];

   //we'll use a 2-D array for constructing the set of block column indices,
   //because we need to keep them grouped by rows, and we aren't guaranteed
   //that they'll be grouped by rows in the file.
   int totalNumBlks = 0;
   int** blkColInds = new int*[N_update_];

   for(i=0; i<N_update_; i++) {
      num_nz_blocks[i] = 0;
      blkColInds[i] = NULL;
   }

   int blkRows, blkCols, rows, cols;
   char line[256];

   do {
      fgets(line,256,infile);
   } while(strchr(line,'%'));
   sscanf(line,"%d %d %d %d",&blkRows, &blkCols, &rows, &cols);

   if ((blkRows != blkCols) || (rows != cols))
      messageAbort("readAllocateInfo: non-square matrix not allowed.");

   int br, bc, pr, pc, index;

   while (!feof(infile)) {
      do {
         fgets(line,256,infile);
      } while(strchr(line,'%'));

      if(feof(infile))break;

      sscanf(line, "%d %d %d %d", &br, &bc, &pr, &pc);

      if (inUpdate(br, index)) {
         if ((bc < 0) || bc >= blkCols) {
            char mesg[80];
            sprintf(mesg,"readAllocateInfo: blkCols %d, 0-based col ind %d",
                    blkCols, bc);
            fclose(infile);
            messageAbort(mesg);
         }
         insertList(bc, blkColInds[index], num_nz_blocks[index]);
         totalNumBlks++;
      }
   }

   //so we've read the whole file, now flatten the 2-D list blkColInds
   //into the required 1-D list blk_col_inds.
   blk_col_inds = new int[totalNumBlks];

   int offset = 0;
   for(i=0; i<N_update_; i++) {
      for(int j=0; j<num_nz_blocks[i]; j++) {
         blk_col_inds[offset++] = blkColInds[i][j];
      }

      delete [] blkColInds[i];
   }

   delete [] blkColInds;
}
//==============================================================================
void AztecDVBR_Matrix::getRemoteBlkSizes(int* remoteBlkSizes,
					 int* remoteInds,
                                         int len)
{
  //
  //remoteInds is a sorted list of indices that correspond to rows
  //in remote processors' update lists. This function will spread the
  //indices to all processors so that they can provide the blk sizes,
  //then spread that information back to all processors.
  //
#ifdef FEI_SER
  return;
#else
  int numProcs = amap_->getProcConfig()[AZ_N_procs];
  int thisProc = amap_->getProcConfig()[AZ_node];
  MPI_Comm comm = amap_->getCommunicator();

   int* lengths = new int[numProcs];
   lengths[0] = 0;

   //gather up the lengths of the lists that each proc will be sending.
   MPI_Allgather(&len, 1, MPI_INT, lengths, 1, MPI_INT, comm);

   //now form a list of the offset at which each proc's contribution will
   //be placed in the all-gathered list.
   int* offsets = new int[numProcs];

   offsets[0] = 0;
   int totalLength = lengths[0];
   for(int i=1; i<numProcs; i++) {
      offsets[i] = offsets[i-1] + lengths[i-1];
      totalLength += lengths[i];
   }

   //now we can allocate the list to recv into.
   int* recvBuf = new int[totalLength];

   //now we're ready to do the gather.
   MPI_Allgatherv(remoteInds, len, MPI_INT, recvBuf, lengths, offsets,
                  MPI_INT, comm);

   //now we'll run through the list and put block sizes into a list of
   //the same length as the total recvBuf list.
   int* blkSizes = new int[totalLength];
   int index;

   for(int j=0; j<totalLength; j++) {
      if (inUpdate(recvBuf[j], index)) {
         blkSizes[j] = Amat_->rpntr[index+1]-Amat_->rpntr[index];
      }
      else blkSizes[j] = 0;
   }

   //now we'll reduce this info back onto all processors. We'll use MPI_SUM.
   //Since the sizes we did NOT supply hold a 0, and each spot in the list
   //should only have a nonzero size from 1 processor, the result will be
   //that each spot in the result list has the correct value.
   int* recvSizes = new int[totalLength];

   MPI_Allreduce(blkSizes, recvSizes, totalLength, MPI_INT, MPI_SUM, comm);

   //and finally, we just need to run our section of the list of recv'd sizes,
   //and transfer them into the remoteBlkSizes list.
   int offset = offsets[thisProc];
   for(int k=0; k<len; k++) {
      remoteBlkSizes[k] = recvSizes[offset + k];
      if (recvSizes[offset+k] <= 0)
         messageAbort("getRemoteBlkSizes: recvd a size <= 0.");
   }

   delete [] lengths;
   delete [] offsets;
   delete [] recvBuf;
   delete [] blkSizes;
   delete [] recvSizes;
#endif
}
// update for input-only block/stream algorithms
void BSafe::BSafeContext::update(const CssmData &data)
{
	opStarted = true;
    check(inUpdate(bsAlgorithm, POINTER(data.data()), data.length(), bsSurrender));
}