/* ---------------------------------------------------------------------
*
*  -- 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__
void PB_CVMcontig( PB_VM_T * VM, Int * NRPQ, Int * NCPQ, Int * IOFF,
                   Int * JOFF )
#else
void PB_CVMcontig( VM, NRPQ, NCPQ, IOFF, JOFF )
/*
*  .. Scalar Arguments ..
*/
   Int            * IOFF, * JOFF, * NCPQ, * NRPQ;
   PB_VM_T        * VM;
#endif
{
/*
*  Purpose
*  =======
*
*  PB_CVMcontig  computes  the  maximum  number  of  contiguous rows and
*  columns  corresponding  to  the  first diagonals of the local virtual
*  matrix VM. This routine also returns the row and column offset of the
*  first diagonal entry.
*
*  Arguments
*  =========
*
*  VM      (local input) pointer to a PB_VM_T structure
*          On entry,  VM  is  a  pointer to a structure of type PB_VM_T,
*          that contains the virtual matrix information (see pblas.h).
*
*  NRPQ    (local output) INTEGER
*          On exit, NRPQ specifies the number of contiguous rows corres-
*          ponding  to  the  first diagonals of the local virtual matrix
*          VM. On exit, NRPQ is at least zero.
*
*  NCPQ    (local output) INTEGER
*          On exit, NCPQ specifies the number of contiguous columns cor-
*          responding to the first diagonals of the local virtual matrix
*          VM. On exit, NRPQ is at least zero.
*
*  IOFF    (local output) INTEGER
*          On exit, IOFF is  the  local row offset of the first row cor-
*          responding to a  diagonal  entry of the Virtual matrix VM. If
*          no diagonals are found, the  value zero is returned. On exit,
*          IOFF is at least zero.
*
*  JOFF    (local output) INTEGER
*          On exit, JOFF is the local column offset of the first  column
*          corresponding  to  a diagonal entry of the Virtual matrix VM.
*          If no  diagonals  are found,  the  value zero is returned. On
*          exit, JOFF is at least zero.
*
*  -- Written on April 1, 1998 by
*     Antoine Petitet, University of Tennessee, Knoxville 37996, USA.
*
*  ---------------------------------------------------------------------
*/
/*
*  .. Local Scalars ..
*/
   Int            ColCont=1, FirstD=0, GoSouth, GoEast, RowCont=1, ilow, imbloc,
                  inbloc, iupp, lcmt, lcmtnn=0, lcmt00, lmbloc, lnbloc, low, mb,
                  mblks, mbloc, mcur=0, mcurd, md=0, nb, nblks, nbloc, ncur=0,
                  ncurd, nd=0, npq=0, pmb, qnb, tmp1, tmp2, upp;
/* ..
*  .. Executable Statements ..
*
*/
   *NRPQ = 0; *NCPQ = 0; *IOFF = 0; *JOFF = 0;
   mblks = VM->mblks; nblks = VM->nblks;
/*
*  Quick return if I don't own any blocks.
*/
   if( ( mblks == 0 ) || ( nblks == 0 ) ) return;
/*
*  Retrieve the contents of VM structure fields
*/
   lcmt00 = VM->lcmt00;
   imbloc = VM->imbloc; mb  = VM->mb;  lmbloc = VM->lmbloc;
   iupp   = VM->iupp;   upp = VM->upp; pmb    = VM->nprow * mb;
   inbloc = VM->inbloc; nb  = VM->nb;  lnbloc = VM->lnbloc;
   ilow   = VM->ilow;   low = VM->low; qnb    = VM->npcol * nb;
/*
*  Handle separately the first row and/or column of the LCM table. Update the
*  LCM value of the curent block lcmt00, as well as the coordinates of the
*  current entry in the LCM table (mcur,ncur).
*/
   GoSouth = ( lcmt00 > iupp );
   GoEast  = ( lcmt00 < ilow );
/*
*  Go through the table looking for blocks owning diagonal entries.
*/
   if( !( GoSouth ) && !( GoEast ) )
   {
/*
*  The upper left block owns diagonal entries lcmt00 >= ilow && lcmt00 <= iupp
*  Compute the number of diagonals in this block as well as lcm value (lcntnn)
*  that its neighbor should have to preserve continuity.
*/
      if( lcmt00 >= 0 )
      {
         tmp2 = ( ( tmp1 = imbloc - lcmt00 ) > 0 ? tmp1 : 0 );
         if(       tmp2 <  inbloc ) { npq = tmp2;   lcmtnn = -npq;         }
         else if ( tmp2 == inbloc ) { npq = inbloc; lcmtnn = 0;            }
         else                       { npq = inbloc; lcmtnn = lcmt00 + npq; }
         *IOFF += lcmt00;
      }
      else
      {
         tmp2 = ( ( tmp1 = inbloc + lcmt00 ) > 0 ? tmp1 : 0 );
         if(       tmp2 <  imbloc ) { npq = tmp2;   lcmtnn = npq;          }
         else if ( tmp2 == imbloc ) { npq = tmp2;   lcmtnn = 0;            }
         else                       { npq = imbloc; lcmtnn = lcmt00 - npq; }
         *JOFF -= lcmt00;
      }
/*
*  Save coordinates of last block owning diagonals. Set FirstD to one, since
*  a block owning diagonals has been found.
*/
      md = 0; nd = 0; FirstD = 1;
/*
*  Those rows and columns are obviously contiguous
*/
      *NRPQ = *NCPQ = npq;
/*
*  Decide whether one should go south or east in the table: Go east if the
*  block below the current one only owns lower entries. If this block, however,
*  owns diagonals, then go south.
*/
      GoSouth = !( GoEast = ( lcmt00 - iupp + upp - pmb < ilow ) );
   }

   if( GoSouth )
   {
/*
*  Go one step south in the LCM table. Adjust the current LCM value.
*/
      lcmt00 -= iupp - upp + pmb; mcur++;
      if( !FirstD ) *IOFF += imbloc;
/*
*  While there are blocks remaining that own upper entries, keep going south.
*  Adjust the current LCM value accordingly.
*/
      while( ( mcur < mblks ) && ( lcmt00 > upp ) )
      {
         lcmt00 -= pmb;
         mcur++;
         if( !FirstD ) *IOFF += mb;
      }
/*
*  Return if no more row in the LCM table.
*/
      if( mcur >= mblks ) goto l_end;
/*
*  lcmt00 <= upp. The current block owns either diagonals or lower entries.
*  Save the current position in the LCM table. After this column has been
*  completely taken care of, re-start from this row and the next column of
*  the LCM table.
*/
      lcmt = lcmt00; mbloc = mb; mcurd = mcur;

      while( ( mcurd < mblks ) && ( lcmt >= ilow ) )
      {
         if( mcurd == mblks-1 ) mbloc = lmbloc;
/*
*  A block owning diagonals lcmt00 >= ilow && lcmt00 <= upp has been found.
*  If this is not the first one, update the booleans telling if the rows
*  and/or columns are contiguous.
*/
         if( FirstD )
         {
            RowCont = RowCont &&
            ( ( ( mcurd == md+1 ) &&  ( lcmtnn  <= 0 )  &&  ( lcmt <= 0 ) ) ||
              ( ( mcurd == md ) && ( ncur == nd+1 ) && ( lcmtnn == lcmt ) ) );
            ColCont = ColCont &&
            ( ( ( ncur == nd+1 )  &&  ( lcmtnn  >= 0 )  &&  ( lcmt >= 0 ) ) ||
              ( ( ncur == nd ) && ( mcurd == md+1 ) && ( lcmtnn == lcmt ) ) );
         }
/*
*  Compute the number of diagonals in this block as well as lcm value (lcntnn)
*  that its neighbor should have to preserve continuity.
*/
         if( lcmt >= 0 )
         {
            tmp2 = ( ( tmp1 = mbloc - lcmt ) > 0 ? tmp1 : 0 );
            if(       tmp2 <  inbloc ) { npq = tmp2;   lcmtnn = -npq;       }
            else if ( tmp2 == inbloc ) { npq = inbloc; lcmtnn = 0;          }
            else                       { npq = inbloc; lcmtnn = lcmt + npq; }
            if( !FirstD ) *IOFF += lcmt;
         }
         else
         {
            tmp2 = ( ( tmp1 = inbloc + lcmt ) > 0 ? tmp1 : 0 );
            if(       tmp2 <  mbloc ) { npq = tmp2;  lcmtnn = npq;        }
            else if ( tmp2 == mbloc ) { npq = tmp2;  lcmtnn = 0;          }
            else                      { npq = mbloc; lcmtnn = lcmt - npq; }
            if( !FirstD ) *JOFF -= lcmt;
         }
/*
*  Save coordinates of last block owning diagonals. Set FirstD to one, since
*  a block owning diagonals has been found.
*/
         md = mcurd; nd = ncur; FirstD = 1;
/*
*  If rows (resp columns) are still contiguous, add those npq rows (resp.
*  columns).
*/
         if( RowCont ) *NRPQ += npq;
         if( ColCont ) *NCPQ += npq;
/*
*  Keep going south until there are no more blocks owning diagonals
*/
         lcmt00  = lcmt;
         lcmt   -= pmb;
         mcur    = mcurd++;
      }
/*
*  I am done with the first column of the LCM table. Go to the next column.
*/
      lcmt00 += low - ilow + qnb; ncur++;
      if( !FirstD ) *JOFF += inbloc;
   }
   else if( GoEast )
   {
/*
*  Go one step east in the LCM table. Adjust the current LCM value.
*/
      lcmt00 += low - ilow + qnb; ncur++;
      if( !FirstD ) *JOFF += inbloc;
/*
*  While there are blocks remaining that own lower entries, keep going east
*  in the LCM table. Adjust the current LCM value.
*/
      while( ( ncur < nblks ) && ( lcmt00 < low ) )
      {
         lcmt00 += qnb;
         ncur++;
         if( !FirstD ) *JOFF += nb;
      }
/*
*  Return if no more column in the LCM table.
*/
      if( ncur >= nblks ) goto l_end;
/*
*  lcmt00 >= low. The current block owns either diagonals or upper entries. Save
*  the current position in the LCM table. After this row has been completely
*  taken care of, re-start from this column and the next row of the LCM table.
*/
      lcmt = lcmt00; nbloc = nb; ncurd = ncur;

      while( ( ncurd < nblks ) && ( lcmt <= iupp ) )
      {
         if( ncurd == nblks-1 ) nbloc = lnbloc;
/*
*  A block owning diagonals lcmt00 >= low && lcmt00 <= iupp has been found.
*  If this is not the first one, update the booleans telling if the rows
*  and/or columns are contiguous.
*/
         if( FirstD )
         {
            RowCont = RowCont &&
            ( ( ( mcur == md+1 )  &&   ( lcmtnn <= 0 )  &&  ( lcmt <= 0 ) ) ||
              ( ( mcur == md ) && ( ncurd == nd+1 ) && ( lcmtnn == lcmt ) ) );
            ColCont = ColCont &&
            ( ( ( ncurd == nd+1 ) &&   ( lcmtnn >= 0 )  &&  ( lcmt >= 0 ) ) ||
              ( ( ncurd == nd ) && ( mcur == md+1 ) && ( lcmtnn == lcmt ) ) );
         }
/*
*  Compute the number of diagonals in this block as well as lcm value (lcntnn)
*  that its neighbor should have to preserve continuity.
*/
         if( lcmt >= 0 )
         {
            tmp2 = ( ( tmp1 = imbloc - lcmt ) > 0 ? tmp1 : 0 );
            if(       tmp2 <  nbloc ) { npq = tmp2;  lcmtnn = -npq;       }
            else if ( tmp2 == nbloc ) { npq = nbloc; lcmtnn = 0;          }
            else                      { npq = nbloc; lcmtnn = lcmt + npq; }
            if( !FirstD ) *IOFF += lcmt;
         }
         else
         {
            tmp2 = ( ( tmp1 = nbloc + lcmt ) > 0 ? tmp1 : 0 );
            if(       tmp2 <  imbloc ) { npq = tmp2;   lcmtnn = npq;        }
            else if ( tmp2 == imbloc ) { npq = tmp2;   lcmtnn = 0;          }
            else                       { npq = imbloc; lcmtnn = lcmt - npq; }
            if( !FirstD ) *JOFF -= lcmt;
         }
/*
*  Save coordinates of last block owning diagonals. Set FirstD to one, since
*  a block owning diagonals has been found.
*/
         md = mcur; nd = ncurd; FirstD = 1;
/*
*  If rows (resp columns) are still contiguous, add those npq rows (resp.
*  columns).
*/
         if( RowCont ) *NRPQ += npq;
         if( ColCont ) *NCPQ += npq;
/*
*  Keep going east until there are no more blocks owning diagonals.
*/
         lcmt00 = lcmt; lcmt += qnb; ncur = ncurd++;
      }
/*
*  I am done with the first row of the LCM table. Go to the next row.
*/
      lcmt00 -= iupp - upp + pmb; mcur++;
      if( !FirstD ) *IOFF += imbloc;
   }
/*
*  Loop over the remaining columns of the LCM table.
*/
   nbloc = nb;

   while( ( RowCont || ColCont ) && ( ncur  < nblks ) )
   {
      if( ncur == nblks-1 ) nbloc = lnbloc;
/*
*  While there are blocks remaining that own upper entries, keep going south.
*  Adjust the current LCM value accordingly.
*/
      while( ( mcur  < mblks ) && ( lcmt00 > upp ) )
      {
         lcmt00 -= pmb;
         mcur++;
         if( !FirstD ) *IOFF += mb;
      }
/*
*  Return if no more row in the LCM table.
*/
      if( mcur >= mblks ) goto l_end;
/*
*  lcmt00 <= upp. The current block owns either diagonals or lower entries.
*  Save the current position in the LCM table. After this column has been
*  completely taken care of, re-start from this row and the next column of
*  the LCM table.
*/
      lcmt = lcmt00; mbloc = mb; mcurd = mcur;

      while( ( mcurd < mblks ) && ( lcmt >= low ) )
      {
         if( mcurd == mblks-1 ) mbloc = lmbloc;
/*
*  A block owning diagonals lcmt00 >= low && lcmt00 <= upp has been found.
*  If this is not the first one, update the booleans telling if the rows
*  and/or columns are contiguous.
*/
         if( FirstD )
         {
            RowCont = RowCont &&
            ( ( ( mcurd == md+1 )  &&  ( lcmtnn <= 0 )  &&  ( lcmt <= 0 ) ) ||
              ( ( mcurd == md ) && ( ncur == nd+1 ) && ( lcmtnn == lcmt ) ) );
            ColCont = ColCont &&
            ( ( ( ncur == nd+1 )   &&  ( lcmtnn >= 0 )  &&  ( lcmt >= 0 ) ) ||
              ( ( ncur == nd ) && ( mcurd == md+1 ) && ( lcmtnn == lcmt ) ) );
         }
/*
*  Compute the number of diagonals in this block as well as lcm value (lcntnn)
*  that its neighbor should have to preserve continuity.
*/
         if( lcmt >= 0 )
         {
            tmp2 = ( ( tmp1 = mbloc - lcmt ) > 0 ? tmp1 : 0 );
            if(       tmp2 <  nbloc ) { npq = tmp2;  lcmtnn = -npq;       }
            else if ( tmp2 == nbloc ) { npq = nbloc; lcmtnn = 0;          }
            else                      { npq = nbloc; lcmtnn = lcmt + npq; }
            if( !FirstD ) *IOFF += lcmt;
         }
         else
         {
            tmp2 = ( ( tmp1 = nbloc + lcmt ) > 0 ? tmp1 : 0 );
            if(       tmp2 <  mbloc ) { npq = tmp2;  lcmtnn = npq;        }
            else if ( tmp2 == mbloc ) { npq = tmp2;  lcmtnn = 0;          }
            else                      { npq = mbloc; lcmtnn = lcmt - npq; }
            if( !FirstD ) *JOFF -= lcmt;
         }
/*
*  Save coordinates of last block owning diagonals. Set FirstD to one, since
*  a block owning diagonals has been found.
*/
         md = mcurd; nd = ncur; FirstD = 1;
/*
*  If rows (resp columns) are still contiguous, add those npq rows (resp.
*  columns).
*/
         if( RowCont ) *NRPQ += npq;
         if( ColCont ) *NCPQ += npq;
/*
*  Keep going south until there are no more blocks owning diagonals
*/
         lcmt00  = lcmt;
         lcmt   -= pmb;
         mcur    = mcurd++;
      }
/*
*  I am done with this column of the LCM table. Go to the next column until
*  there are no more column in the table.
*/
      lcmt00 += qnb; ncur++;
      if( !FirstD ) *JOFF += nb;
   }

l_end:
/*
*  If no diagonals were found, reset IOFF and JOFF to zero.
*/
   if( !FirstD ) { *IOFF = 0; *JOFF = 0; }
/*
*  End of PB_CVMcontig
*/
}
