/* ---------------------------------------------------------------------
*
*  -- PBLAS auxiliary routine (version 2.0) --
*     University of Tennessee, Knoxville, Oak Ridge National Laboratory,
*     and University of California, Berkeley.
*     April 1, 1998
*
*  ---------------------------------------------------------------------
*/
/*
*  Include files
*/
#include "../pblas.h"
#include "../PBpblas.h"
#include "../PBtools.h"
#include "../PBblacs.h"
#include "../PBblas.h"

#ifdef __STDC__
Int PB_Cnpreroc( Int N, Int I, Int INB, Int NB, Int PROC, Int SRCPROC,
                 Int NPROCS )
#else
Int PB_Cnpreroc( N, I, INB, NB, PROC, SRCPROC, NPROCS )
/*
*  .. Scalar Arguments ..
*/
   Int            I, INB, N, NB, NPROCS, PROC, SRCPROC;
#endif
{
/*
*  Purpose
*  =======
*
*  PB_Cnpreroc computes  the  number of preceeding rows or columns of a
*  submatrix  that  are  possessed by processes closer to SRCPROC1 than
*  PROC where SRCPROC1 is the process owning the row or column globally
*  indexed by I. The submatrix is defined by giving out N  rows/columns
*  starting from global index I.  Therefore, if  SRCPROC=0 and  PROC=4,
*  then PB_Cnpreroc returns the number of matrix rows or columns  owned
*  by processes 0, 1, 2, and 3.
*
*  Arguments
*  =========
*
*  N       (global input) INTEGER
*          On entry, N  specifies the number of rows/columns being dealt
*          out. N must be at least zero.
*
*  I       (global input) INTEGER
*          On entry, I  specifies the global index of the matrix  entry.
*          I must be at least zero.
*
*  INB     (global input) INTEGER
*          On entry,  INB  specifies  the size of the first block of the
*          global matrix distribution. INB must be at least one.
*
*  NB      (global input) INTEGER
*          On entry, NB specifies the size of the blocks used to  parti-
*          tion the matrix. NB must be at least one.
*
*  PROC    (local input) INTEGER
*          On entry, PROC specifies  the coordinate of the process whose
*          local portion is determined.  PROC must be at least zero  and
*          strictly less than NPROCS.
*
*  SRCPROC (global input) INTEGER
*          On entry,  SRCPROC  specifies  the coordinate of the  process
*          that possesses the  first row or column  of the matrix.  When
*          SRCPROC = -1, the data  is not  distributed  but  replicated,
*          otherwise  SRCPROC  must be at least zero and  strictly  less
*          than NPROCS.
*
*  NPROCS  (global input) INTEGER
*          On entry,  NPROCS  specifies the total number of process rows
*          or columns over which the matrix is distributed.  NPROCS must
*          be at least one.
*
*  -- Written on April 1, 1998 by
*     Antoine Petitet, University of Tennessee, Knoxville 37996, USA.
*
*  ---------------------------------------------------------------------
*/
/*
*  .. Local Scalars ..
*/
   Int            ilocblk, mydist, nblocks;
/* ..
*  .. Executable Statements ..
*
*/
   if( ( SRCPROC == -1 ) || ( NPROCS == 1 ) )
/*
*  The data is not distributed, or there is just one process in this dimension
*  of the grid.
*/
      return( 0 );
/*
*  Compute coordinate of process owning I and corresponding INB
*/
   if( ( INB -= I ) <= 0 )
   {
/*
*  I is not in first block, find out which process has it and update size of
*  first block
*/
      nblocks  = ( -INB ) / NB + 1;
      SRCPROC += nblocks;
      SRCPROC -= ( SRCPROC / NPROCS ) * NPROCS;
      INB     += nblocks * NB;
   }
/*
*  Now everything is just like N, I=0, INB, NB, SRCPROC, NPROCS. If I am the
*  source process, nothing preceeds me ...
*/
   if( PROC == SRCPROC ) return( 0 );
/*
*  If SRCPROC owns the N rows or columns, then return N since I cannot be the
*  source process anymore.
*/
   if( N <= INB ) return( N );
/*
*  Find out how many full blocks are globally (nblocks) and locally (ilocblk)
*  in those N entries.
*/
   nblocks = ( N - INB ) / NB + 1;
/*
*  Compute my distance from the source process so that within this process
*  coordinate system, the source process is the process such that mydist=0.
*/
   if( ( mydist = PROC - SRCPROC ) < 0 ) mydist += NPROCS;
/*
*  When mydist < nblocks - ilocblk * NPROCS, I own ilocblk + 1 full blocks,
*  when mydist > nblocks - ilocblk * NPROCS, I own ilocblk     full blocks,
*  when mydist = nblocks - ilocblk * NPROCS, either the last block is not full
*  and I own it, or the last block is full and I am the first process owning
*  only ilocblk full blocks.
*
*  Therefore, when 0 < mydist <= nblocks - ilocblk * NPROCS, the number of rows
*  or columns preceeding me is INB + ilocblk*NB + (mydist-1)*(ilocblk+1)*NB,
*  i.e. INB - NB + ( ilocblk+1 ) * NB * mydist. Otherwise, there are exactly
*  NB * ilocblk * ( NPROCS - mydist ) rows or columns after me including mine,
*  i.e N + NB * ilocblk * ( mydist - NPROCS ) rows or columns preceeding me.
*/
   if( nblocks < NPROCS )
      return( ( ( mydist <= nblocks ) ? INB + NB * ( mydist - 1 ) : N ) );

   ilocblk = nblocks / NPROCS;
   return( ( ( mydist <= ( nblocks - ilocblk * NPROCS ) ) ?
             INB - NB + ( ilocblk + 1 ) * NB * mydist :
             N + NB * ilocblk * ( mydist - NPROCS ) ) );
/*
*  End of PB_Cnpreroc
*/
}
