/****************************************************************************
*                                                                           *
* Copyright (C) 2024 Intel Corporation                                      *
*                                                                           *
*****************************************************************************

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 copyright holder nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR 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.

 ***************************************************************************

For more documentation than found here, see

[1] doc/ReadMe_IMB.txt 

[2] Intel(R) MPI Benchmarks
    Users Guide and Methodology Description
    In 
    doc/IMB_Users_Guide.pdf

 A collection of auxiliary functions ONLY for the CHECK mode of IMB
 ===================================================================

 File: IMB_chk_diff.c 

 Implemented functions: 

 IMB_chk_dadd;
 IMB_ddiff;
 IMB_show;
 IMB_err_msg;
 IMB_chk_diff;
 IMB_cmp_cat;
 IMB_chk_contiguous;
 IMB_chk_distr;
 IMB_chk_contained;
 IMB_compute_crc;

 ***************************************************************************/





#include "IMB_declare.h"
#include "IMB_benchmark.h"


#include "IMB_prototypes.h"

#include <limits.h>

static int asize = (int) sizeof(assign_type);

void IMB_chk_dadd(void* AUX, int Locsize, size_t buf_pos,
                  int rank0, int rank1) {
/*

                      Auxiliary, only for checking mode;
                      Creates reference accumulated values in a given
                      buffer section, accumulated over given processor ranks

Input variables:

-Locsize              (type int)
                      Size of buffer section to check


-buf_pos              (type int)
                      Start position of buffer section


-rank0                (type int)
-rank1                (type int)
                      Process' values between rank0 and rank1 are accumulated



In/out variables:

-AUX                  (type void*)
                      Contains accumulated values

*/
    /* Sum up all process' data buffers in certain window */
    int  rank;
    size_t i;

    for (i = 0; i < Locsize / asize; i++)
        ((assign_type*)AUX)[i] = 0.;

    for (rank = rank0; rank <= rank1; rank++) {
        for (i = 0; i < Locsize / asize; i++)
            ((assign_type*)AUX)[i] += BUF_VALUE(rank, buf_pos / asize + i);
    }
}


double IMB_ddiff(assign_type *A, assign_type *B, size_t len,
                 size_t *fault_pos) {
/*

                          Compares the values of 2 buffers A, B and returns max. diff

Input variables:

-A                        (type assign_type *)
                          Buffer of values

-B                        (type assign_type *)
                          Another buffer of values to be checked against A

-len                      (type int)
                          Length (in assign_type items) of A, B

Output variables:

-fault_pos                (type int *)
                          Position of first non tolerable deviation

Return value              (type double)
                          Deviation of A and B

*/
    /* max. relative difference of vectors A/B */
    double D, d1, rel;
    size_t i;

    D = 0.;

    d1 = -1.;
    *fault_pos = CHK_NO_FAULT;

    if (len > 0) {
        for (i = 0; i < len && d1 <= TOL; i++) {
            if (A[i] != 0.)
                rel = A_ABS(A[i]);
            else
                rel = 1.;
            d1 = A_ABS(A[i] - B[i]) / rel;
        }
        D = d1;

        if (D > TOL) {
            D = 1.;
            IMB_Assert(i > 0);
            *fault_pos = (i - 1)*asize;
        }
    }
    return D;
}


void IMB_show(char* text, struct comm_info* c_info, void* buf,
              size_t loclen, size_t totlen, int j_sample,
              POSITIONING fpos) {
/*

                      Shows an excerpt of erroneous buffer if occurs in check mode

Input variables:

-text                 (type char*)
                      Accompanying text to put out

-loclen               (type int)
                      Local length of buffer

-totlen               (type int)
                      Total length of buffer (for gathered or shared access buffers)

-j_sample             (type int)
                      Number of sample the error occurred

-fpos                 (type POSITIONING)
                      File positionning (if relevant)

-c_info               (type struct comm_info*)
                      Collection of all base data for MPI;
                      see [1] for more information

-buf                  (type void*)
                      Given check buffer

*/
    size_t i;

    fprintf(unit, "Process %d: %s", c_info->rank, text);
    fprintf(unit, "\n");

#ifdef DEBUG
    size_t j;
    fprintf(dbg_file, "Process %d: %s", c_info->rank, text);
    fprintf(dbg_file, "\n");
#endif /*DEBUG*/

#ifdef MPIIO
    {
        MPI_Offset Offset;
        switch (fpos) {
            case indv_block:
                Offset = (MPI_Offset)(j_sample * totlen);
                break;

            case explic:
                Offset = c_info->split.Offset + (MPI_Offset)(j_sample * totlen);
                break;

            case priv:
                Offset = (MPI_Offset)(j_sample * loclen);
                break;

            case shared:
                Offset = (MPI_Offset)(-1);
                break;

            case nothing:
                exit(1);
        }

        if (fpos == shared)
            fprintf(unit,
#ifdef WIN_IMB
            "Overall size = %I64u,"
            " Portion = %I64u,"
#else
            "Overall size = %lu,"
            " Portion = %lu,"
#endif /* WIN_IMB*/
            " #sample= %d\n",
            totlen, loclen, j_sample);
        else if (fpos != -1)
            fprintf(unit,
#ifdef WIN_IMB
            "Overall size = %I64u,"
            " Portion = %I64u,"
#else
            "Overall size = %lu,"
            " Portion = %lu,"
#endif /* WIN_IMB*/
            " Startpos = %ld\n",
            totlen, loclen, (long)Offset);

#ifdef DEBUG
        if (fpos == shared)
            fprintf(dbg_file,
#ifdef WIN_IMB
            "Overall size = %I64u,"
            " Portion = %I64u,"
#else
            "Overall size = %lu,"
            " Portion = %lu,"
#endif /*WIN_IMB*/
            " #sample= %d\n",
            totlen, loclen, j_sample);
        else if (fpos != -1)
            fprintf(dbg_file,
#ifdef WIN_IMB
            "Overall size = %I64u,"
            " Portion = %I64u,"
#else
            "Overall size = %lu,"
            " Portion = %lu,"
#endif /*WIN_IMB*/
            " Startpos = %ld\n",
            totlen, loclen, (long)Offset);
#endif /*DEBUG*/

    }
#endif /*MPIIO*/

    if (loclen < asize) {
        if (loclen == 0) {
            fprintf(unit, "Buffer empty\n");
#ifdef DEBUG
            fprintf(dbg_file, "Buffer empty\n");
#endif
        } else {
            fprintf(unit, "Buffer in bytewise int representation: ");

            for (i = 0; i < loclen; i++)
                fprintf(unit, "%d ", ((char*)buf)[i]);

            fprintf(unit, "\n");
#ifdef DEBUG
            fprintf(dbg_file, "Buffer in bytewise int representation: ");

            for (i = 0; i < loclen; i++)
                fprintf(dbg_file, "%d ", ((char*)buf)[i]);
            fprintf(dbg_file, "\n");
#endif /*DEBUG*/
        }
    } else {
        if (loclen >= 2 * asize)
            fprintf(unit, "Buffer, 1st and last entry: ");
        else
            fprintf(unit, "Buffer entry: ");

#ifdef BUFFERS_INT
        fprintf(unit, "%d ", ((assign_type*)buf)[0]);
        if (loclen >= 2 * asize)
            fprintf(unit, "%d ", ((assign_type*)buf)[loclen / asize - 1]);
#endif

#ifdef BUFFERS_FLOAT
        fprintf(unit, "%f ", ((assign_type*)buf)[0]);
        if (loclen >= 2 * asize)
            fprintf(unit, "%f ", ((assign_type*)buf)[loclen / asize - 1]);
#endif

        fprintf(unit, "\n");

#ifdef DEBUG
        if (err_flag)
            for (j = 0; j < loclen / asize; j += 5) {
#ifdef BUFFERS_INT
                for (i = j; i < min(loclen / asize, j + 5); i++)
                    fprintf(dbg_file, "%d ", ((assign_type*)buf)[i]);
#endif /*BUFFERS_INT*/
#ifdef BUFFERS_FLOAT
                for (i = j; i < min(loclen / asize, j + 5); i++)
                    fprintf(dbg_file, "%f ", ((assign_type*)buf)[i]);
#endif /*BUFFERS_FLOAT*/
                fprintf(dbg_file, "\n");
            } /*for*/*/
#endif /*DEBUG*/
    }
    fflush(unit);

#ifdef DEBUG
    fflush(dbg_file);
#endif

}

void IMB_err_msg(struct comm_info* c_info, char* text, size_t totsize,
                 int j_sample) {
/*

                      Outputs an brief error diagnostics if occurs

Input variables:

-c_info               (type struct comm_info*)
                      Collection of all base data for MPI;
                      see [1] for more information

-text                 (type char*)
                      Accompanying text

-totsize              (type int)
                      Size of the erroneous buffer

-j_sample             (type int)
                      Number of sample the error occured in

*/
    fprintf(unit, "%d: Error %s,"
#ifdef WIN_IMB
        "size = %I64u,"
#else
        "size = %lu,"
#endif
        "sample #%d\n",
        c_info->rank, text, totsize, j_sample);
}

#ifdef CHECK

void IMB_chk_diff(char* text, struct comm_info* c_info, void* RECEIVED,
                  size_t buf_pos, int Locsize, size_t Totalsize,
                  int unit_size, DIRECTION mode, POSITIONING fpos,
                  int n_sample, int j_sample, int source,
                  double* diff) {
/*

                          Checks a received buffer against expected ref values

Input variables:

-text                     (type char*)
                          Accompanying text


-c_info                   (type struct comm_info*)
                          Collection of all base data for MPI;
                          see [1] for more information


-RECEIVED                 (type void*)
                          The buffer to be checked


-buf_pos                  (type int)
                          Beginning position (in units -> unit_size)


-Locsize                  (type int)
                          Local buffer size


-Totalsize                (type int)
                          Total buffer size (in case of gathered buffers)


-unit_size                (type int)
                          Base unit for positioning


-mode                     (type DIRECTION)
                          Direction of the action that took place


-fpos                     (type POSITIONING)
                          File positioning of the action that took place (if relevant)


-n_sample                 (type int)
                          # overall samples


-j_sample                 (type int)
                          current sample


-source                   (type int)
                          Sending process (if relevant)



Output variables:

-diff                     (type double*)
                          The error against expected values

*/
#ifdef MPIIO
    MPI_File    restore;
    MPI_Status  stat;
    double      def_tmp;
    int         j, j1, j2, ierr, rank, allpos;
    size_t      pos1, pos2;
    int*        rankj;
    size_t*     lenj;
#endif

    double defloc;

    size_t faultpos, pos;

    int    *all_ranks, Npos;
    size_t *lengths;

    if (err_flag) return;

    defloc = 0.;
    faultpos = CHK_NO_FAULT;

    if (Totalsize == 0) {
        *diff = 0.;
        return;
    }

#ifdef MPIIO

    MPI_Barrier(c_info->File_comm);

    if (mode == put) {
        if (c_info->File_rank == 0) {

            IMB_alloc_buf(c_info, "Write check", 0, Totalsize);

            ierr = MPI_File_open(MPI_COMM_SELF, c_info->filename,
                                 c_info->amode, c_info->info, &restore);
            IMB_err_hand(1, ierr);

            if (source == -3) {
                IMB_i_alloc(size_t, lengths, c_info->File_num_procs*n_sample + n_sample - 1, "chk_diff 1");
                IMB_i_alloc(int, all_ranks, c_info->File_num_procs*n_sample + n_sample - 1, "chk_diff 2");
            } else {
                IMB_i_alloc(size_t, lengths, c_info->File_num_procs, "chk_diff 1a");
                IMB_i_alloc(int, all_ranks, c_info->File_num_procs, "chk_diff 2a");
            }

            lenj  = lengths;
            rankj = all_ranks;
            allpos = 0;

            if (j_sample < 0) {
                j1 = 0;
                j2 = n_sample - 1;
            } else {
                j1 = j_sample;
                j2 = j_sample;
            }

            MPI_ERRHAND(MPI_File_seek(restore, (MPI_Offset)j1*(MPI_Offset)Totalsize, MPI_SEEK_SET));

            for (j = j1; j <= j2 && faultpos == CHK_NO_FAULT /*faultpos<0*/; j++) {
                IMB_Assert(Totalsize <= INT_MAX);

                MPI_ERRHAND(MPI_File_read(restore, c_info->r_buffer, (int)Totalsize, c_info->etype, &stat));

                RECEIVED = c_info->r_buffer;

                if (source == -3) {
                    IMB_cmp_cat(c_info, RECEIVED, Totalsize, buf_pos, unit_size, 1,
                                lenj, rankj, &Npos, &faultpos, &def_tmp);

                    lenj += Npos;
                    rankj += Npos;
                    allpos += Npos;
                } else {
                    IMB_cmp_cat(c_info, RECEIVED, Totalsize, buf_pos, unit_size, 0,
                                lenj, rankj, &Npos, &faultpos, &def_tmp);
                }

                defloc = max(defloc, def_tmp);

            } /*for( j=j...*/

            MPI_File_close(&restore);


            j_sample = j - 1;

            IMB_free_aux();

            if ( /*faultpos >= 0*/ faultpos != CHK_NO_FAULT) {

                IMB_err_msg(c_info, text, Totalsize, j_sample);
                fprintf(unit,
                    "Error: restored buffer from output file, invalid portion starting at pos."
#ifdef WIN_IMB
                    "  %I64u\n",
#else
                    "  %lu\n",
#endif
                    (j_sample*Totalsize) + faultpos);

                AUX = (void*)(((char*)RECEIVED) + faultpos);
                IMB_show("Erroneous data:", c_info, AUX, Totalsize - faultpos, Totalsize - faultpos, j_sample, nothing);

            } else {
                if (source == -3) {
                    IMB_chk_distr(c_info, Totalsize, n_sample, lengths, all_ranks, allpos, &def_tmp);

                    if (def_tmp > 0.) {
                        IMB_err_msg(c_info, text, Totalsize, j_sample);
                        IMB_show("restored buffer from output file, has permuted data: ",
                                 c_info, RECEIVED, Totalsize, Totalsize, j_sample, nothing);
                    }
                }

                defloc = max(defloc, def_tmp);

            } /*if( faultpos >= 0 ) */

            IMB_del_r_buf(c_info);
            IMB_v_free((void**)&lengths);
            IMB_v_free((void**)&all_ranks);
        } /*if( c_info -> File_rank == 0 )*/

        fflush(unit);
    }   /*if( mode == put )*/

    if (mode == get) {
        size_t file_pos;
        file_pos = j_sample * Totalsize;

        *diff = 0.;

        IMB_alloc_aux(Totalsize, " chk_diff 5");
        IMB_init_file_content(AUX, file_pos, file_pos + Totalsize - 1);

        IMB_chk_contained(RECEIVED, Locsize, AUX, Totalsize, &pos, &faultpos, &def_tmp,
                          "Compare received portion with file Content");

        IMB_get_rank_portion(c_info->File_rank, c_info->File_num_procs, Totalsize, asize,
                             &pos1, &pos2);

        if ( /*faultpos >= 0*/ faultpos != CHK_NO_FAULT) { /* the type of faultpos is changed to size_t*/
            err_flag = 1;
            defloc = 1;
            IMB_err_msg(c_info, text, Totalsize, j_sample);
            RECEIVED = (void*)((char*)RECEIVED + faultpos);

            fprintf(unit,
                "File position: "
#ifdef WIN_IMB
                "%I64u\n",
#else
                "%lu\n",
#endif
                file_pos + pos + faultpos);

            IMB_show("Read invalid portion: ", c_info, RECEIVED,
                     Locsize - faultpos, Totalsize, j_sample, fpos);

            AUX = (void*)((char*)AUX + pos + faultpos);

            IMB_show("Expected portion: ", c_info, AUX,
                     Locsize - pos - faultpos, Locsize - pos - faultpos, j_sample, nothing);

            MPI_Gather(&pos, 1, MPI_UNSIGNED_LONG, c_info->rdispl, 1, MPI_INT, 0, c_info->File_comm);
        } else {
            if (source == -2 && Locsize > 0) {
                IMB_get_rank_portion(c_info->File_rank, c_info->File_num_procs, Totalsize, asize,
                                     &pos1, &pos2);
                if (pos1 != pos)
                    defloc = 1;
            }

            if (source == -3) {
                /* Check permuted buffer */

                MPI_Gather(&pos, 1, MPI_INT, c_info->rdispl, 1, MPI_INT, 0, c_info->File_comm);
                MPI_Gather(&Locsize, 1, MPI_INT, c_info->reccnt, 1, MPI_INT, 0, c_info->File_comm);

                if (c_info->File_rank == 0)
                    IMB_chk_contiguous(c_info, c_info->rdispl, c_info->reccnt, &defloc);
                else
                    defloc = 0.;

            }

            if (defloc > 0.)
                IMB_err_msg(c_info, "Wrong portion ordering in read buffer", Totalsize, j_sample);
        } /*if( faultpos >= 0 )*/

        defloc = max(defloc, def_tmp);
    } /*if( mode == get )*/

    MPI_Barrier(c_info->File_comm);

#else /*not  MPIIO*/

    if (source >= 0) {
        IMB_alloc_aux(Totalsize, "chk_diff 6");
        IMB_ass_buf(AUX, source, buf_pos,
                    (buf_pos + Totalsize > 0) ? buf_pos + Totalsize - 1 : 0, 1);


        if (Totalsize < asize) {
            IMB_chk_contained(RECEIVED, Totalsize, AUX, Totalsize, &pos, &faultpos, &defloc,
                              "Compare received with expected portion");

            if ( /*faultpos>=0*/ faultpos != CHK_NO_FAULT) { /* type of faultpos is changed to size_t*/
                faultpos = 0;
                defloc   = 1.;
            }
        } else
            defloc = IMB_ddiff((assign_type *)AUX, (assign_type *)RECEIVED, Totalsize / asize, &faultpos);

    } else if (source == -1) {
        IMB_alloc_aux(Locsize, "chk_diff 7");

        if (mode == local)
            IMB_ass_buf(AUX, c_info->rank, buf_pos, Locsize, 1);
        else
            IMB_chk_dadd(AUX, Locsize, buf_pos, 0, c_info->num_procs - 1);

        defloc = IMB_ddiff((assign_type *)AUX, (assign_type *)RECEIVED, Locsize / asize, &faultpos);

    } else {
        if (source == -2) {
            lengths = NULL;
            all_ranks = NULL;

            IMB_cmp_cat(c_info, RECEIVED, Totalsize, buf_pos, unit_size, 0,
                        lengths, all_ranks, &Npos, &faultpos, &defloc);
        }

    }

    if ( /*faultpos>=0*/ faultpos != CHK_NO_FAULT) { /* type of faultpos is changed to size_t*/
        void* tmp = (void*)(((char *)RECEIVED) + faultpos);

        IMB_err_msg(c_info, text, Totalsize, j_sample);
        IMB_show("Got invalid buffer: ", c_info, tmp, asize, asize, j_sample, -1);

        fprintf(unit,
#ifdef WIN_IMB
            "pos: %I64u\n"
#else
            "pos: %lu\n"
#endif /*WIN_IMB*/
            , faultpos);

        tmp = (void*)(((char *)AUX) + faultpos);

        IMB_show("Expected    buffer: ", c_info, tmp, asize, asize, j_sample, -1);

        defloc = 1;
    }
    IMB_free_aux();
#endif /*MPIIO*/


    if (defloc > TOL)
        err_flag = 1;

    *diff = max(*diff, defloc);

    if (mode == local)
        IMB_ass_buf(RECEIVED, c_info->rank, buf_pos, Locsize - 1, 0);
}


void IMB_cmp_cat(struct comm_info *c_info, void* RECEIVED, size_t size,
                 size_t bufpos, int unit_size, int perm,
                 size_t* lengths, int*ranks, int* Npos,
                 size_t *faultpos, double* diff) {
/*

                      Checks a received buffer which is a concatenation of
                      several processes' buffers

Input variables:

-c_info               (type struct comm_info *)
                      Collection of all base data for MPI;
                      see [1] for more information

-RECEIVED             (type void*)
                      The buffer to be checked

-size                 (type int)
                      Size of the buffer

-bufpos               (type int)
                      First position to check (in units -> unit_size)

-unit_size            (type int)
                      Base unit of positioning

-perm                 (type int)
                      Logical flag: 1 if the different rank's portions
                      are potentially in non natural order (relevant for
                      shared file accesses)

Output variables:

-lengths              (type int*)
                      An array of lengths (of a number of erroneous portions)

-ranks                (type int*)
                      An array of ranks (the erroneous portions belong to)

-Npos                 (type int*)
                      Numer of erroneous portions found (=size of 'lengths' and 'ranks' arrays)

-faultpos             (type int *)
                      Position of first found fault

-diff                 (type double*)
                      Diff value

*/
    int    rank, NP;
    size_t pos1, pos2, pos, rsize, rem_size;

    int chk_ok;
    assign_type *a, *r;

    double tmp_diff;

    *diff = 0.;
    *faultpos = CHK_NO_FAULT;

    if (size == 0) return;

#ifdef MPIIO
    NP = c_info->File_num_procs;
#else
    NP = c_info->num_procs;
#endif

    rsize = (size + asize - 1) / asize * asize;

    IMB_alloc_aux(rsize, "chk_diff 8");

    r = (assign_type*)AUX;
    a = (assign_type*)RECEIVED;

    chk_ok = 0;

    if (perm) {
        *Npos = 0;
        pos = 0;

        /* Check beginning of buffer */

        for (rank = 0; rank < NP && !chk_ok; rank++) {
            if (size > 0) {
                IMB_get_rank_portion(rank, NP, size, unit_size, &pos1, &pos2);

                rsize = pos2 - pos1 + 1;
                IMB_ass_buf(AUX, rank, 0, (rsize > 0) ? rsize - 1 : 0, 1);
            } else {
                rsize = 0;
                pos2 = pos1 = 0;
            }



            IMB_chk_contained(RECEIVED, min(asize, rsize), AUX, rsize,
                              &pos, faultpos, &tmp_diff, NULL);

            /*if(*faultpos < 0 && pos>=0)*/
            /* the type of faultpos and pos is changed to size_t */
            if (*faultpos == CHK_NO_FAULT) {
                if (rsize <= asize)
                    chk_ok = 1;
                else {
                    rem_size = rsize - pos;
                    IMB_chk_contained((void*)(r + pos / asize), rem_size, RECEIVED, rem_size, &pos1,
                                      faultpos, &tmp_diff, "Check of first part of received buffer");

                    /*if( *faultpos < 0 && pos1>=0 )*/
                    /* the type of faultpos and pos is changed to size_t */
                    if (*faultpos == CHK_NO_FAULT) {
                        lengths[*Npos] = rem_size;
                        ranks[*Npos] = rank;
                        pos = rem_size;
                        (*Npos)++;
                        chk_ok = 1;
                    }
                } /*if( rsize <= asize )*/
            } /*if( *faultpos == CHK_NO_FAULT)*/
        } /*for( rank=0...*/

        if (!chk_ok) {
            *faultpos = 0;
            *diff     = 1.;
        }

        while ((pos < size) && chk_ok) {
            chk_ok = 0;

            for (rank = 0; rank < NP && !chk_ok; rank++) {
                IMB_get_rank_portion(rank, NP, size, unit_size, &pos1, &pos2);

                IMB_Assert(pos2 >= pos1);
                rsize = pos2 - pos1 + 1;

                /*if( rsize > 0 )*/
                //{

                rem_size = min(rsize, size - pos);

                IMB_ass_buf(AUX, rank, 0, (rsize>0) ? rsize - 1 : 0, 1);

                IMB_chk_contained(AUX, rem_size, (void*)(a + pos / asize), rem_size, &pos1,
                                  faultpos, &tmp_diff, NULL);

                /*if( *faultpos < 0 && pos1 >= 0 ) */
                /* the type of faultpos and pos is changed to size_t */
                if (*faultpos == CHK_NO_FAULT) {
                    lengths[*Npos] = rem_size;
                    ranks[*Npos] = rank;
                    pos = pos + rsize;
                    (*Npos)++;
                    chk_ok = 1;
                }
                //}   /* end if(rsize>0) */
            }   /* end for(rank..) */

            if (!chk_ok) {
                *faultpos = pos;
                *diff     = 1;
            }
        } /* end while */
    } else { /* end if(perm) */
        size_t curr = 0;
        void*  tmp;

        for (rank = 0; rank < NP; rank++) {
            IMB_get_rank_portion(rank, NP, size, unit_size, &pos1, &pos2);

            if (pos2 >= pos1)
                rsize = pos2 - pos1 + 1;
            else
                rsize = 0;

            tmp = (void*)(((char*)RECEIVED) + curr);

            IMB_ass_buf(AUX, rank, bufpos,
                        (bufpos + rsize>0) ? bufpos + rsize - 1 : 0, 1);

            IMB_chk_contained(AUX, rsize, tmp, rsize, &pos1, faultpos, &tmp_diff, "");
            *diff = max(*diff, tmp_diff);

            /*if(*faultpos<0 && pos1>= 0 )*/
            /* the type of faultpos and pos is changed to size_t */
            if (*faultpos == CHK_NO_FAULT)
                curr += rsize;
            else {
                *faultpos += curr;
                *diff = 1;
                break;
            }
        } /*for( rank=0*/
    } /* else if(!perm) */
}




void IMB_chk_contiguous(struct comm_info *c_info, int* rdispl, int* sizes,
                        double*diff) {
/*

                          Checks whether arrays of displacements/sizes form a
                          contiguous buffer

Input variables:

-c_info                   (type struct comm_info *)
                          Collection of all base data for MPI;
                          see [1] for more information

-rdispl                   (type int*)
                          Array of displacements (one for each process)

-sizes                    (type int*)
                          Array of sizes (one for each process)

Output variables:

-diff                     (type double*)
                          0 if contiguous, 1 else

*/
    int i, j, NP, rank, p;

#ifdef MPIIO
    NP = c_info->File_num_procs;
#else
    NP = c_info->num_procs;
#endif

    for (i = 0; i < NP; i++) {
        for (j = i; j < NP; j++)
            if (rdispl[j] < rdispl[i]) {
                p         = rdispl[i];
                rdispl[i] = rdispl[j];
                rdispl[j] = p;
                p         = sizes[i];
                sizes[i] = sizes[j];
                sizes[j] = p;
            }
    }

    p = 0;
    *diff = 0.;

    for (rank = 0; rank < NP; rank++) {
        if (rdispl[rank] == p || sizes[rank] == 0)
            p = p + sizes[rank];
        else
            *diff = 1.;
    }

    if (*diff > TOL) {
        fprintf(unit, "check of contiguity of received buffer portions failed\n");
        fprintf(unit, "Got the following portions/displacements:\n");

        for (rank = 0; rank < NP; rank++)
            fprintf(unit, "%d / %d; ", sizes[rank], rdispl[rank]);
        fprintf(unit, "\n");
    }
}




void IMB_chk_distr(struct comm_info *c_info, size_t size, int n_sample,
                   size_t* lengths, int* ranks, int Npos,
                   double *diff) {
/*

                      (Only for MPI-IO shared file pointer accesses)
                      Checks whether a found set of section lengths/ranks in
                      a file meets expectations

Input variables:

-c_info               (type struct comm_info *)
                      Collection of all base data for MPI;
                      see [1] for more information


-size                 (type int)
                      Size of buffer


-n_sample             (type int)
                      Number of samples expected in file


-lengths              (type int*)
                      Array of section lengths found


-ranks                (type int*)
                      Array of ranks belonging to sections


-Npos                 (type int)
                      Number of sections

Output variables:

-diff                 (type double *)
                      0 if set is consistent, 1 else

*/
    int i, NP, rank;
    size_t pos1, pos2;

    *diff = 0.;

    if (size == 0) return;

#ifdef MPIIO
    NP = c_info->File_num_procs;
#else
    NP = c_info->num_procs;
#endif

    for (rank = 0; rank < NP; rank++)
        c_info->reccnt[rank] = 0;

    i = 0;
    while (i < Npos) {
        rank = ranks[i];

        IMB_get_rank_portion(rank, NP, size, asize, &pos1, &pos2);

        if (pos2 - pos1 + 1 == lengths[i])
            c_info->reccnt[rank]++;
        else if (i < Npos) {
            if (ranks[i + 1] == rank && ((pos2 - pos1 + 1) == (lengths[i] + lengths[i + 1]))) {
                c_info->reccnt[rank]++;
                i++;
            }
        }

        i++;
    } /*while*/

    for (rank = 0; rank < NP; rank++) {
        IMB_get_rank_portion(rank, NP, size, asize, &pos1, &pos2);
        if (pos2 >= pos1 && c_info->reccnt[rank] != n_sample)
            *diff = 1;
    }

    if (*diff > TOL) {
        fprintf(unit, "check of contiguity of received buffer portions failed\n");
        fprintf(unit, "Got the following portions/from process:\n");

        for (i = 0; i < Npos; i++) {
            fprintf(unit,
#ifdef WIN_IMB
                "%I64u / %d; ",
#else
                "%lu / %d; ",
#endif
                lengths[i], ranks[i]);
        }
        fprintf(unit, "\n");
    }
}

void IMB_chk_contained(void* part, size_t p_size, void* whole,
                       size_t w_size, size_t* pos, size_t* fpos,
                       double* D, char*msg) {
/*

                      Checks whether a buffer part is contained in a larger buffer
                      (exploits uniqueness of buffer values, so check is trivial)

Input variables:

-part                 (type void*)
                      Partial buffer

-p_size               (type int)
                      Size of partial buffer

-whole                (type void*)
                      Whole buffer

-w_size               (type int)
                      Size of whole buffer

-msg                  (type char*)
                      Accompanying message

Output variables:

-pos                  (type int*)
                      Position where partial buffer begins in whole buffer
                      if search was successful

-fpos                 (type int*)
                      Position where first fault occurred when start position was
                      found, but later an error occurred

-D                    (type double*)
                      0 if check positive, 1 else

*/
    assign_type *a_part, *a_whole;
    long pcrc, wcrc;
    size_t w_len, p_len;

    a_part = (assign_type*)part;
    a_whole = (assign_type*)whole;

    *fpos = CHK_NO_FAULT;  /* instead of -1*/
    *D = 0.;

    if ( /*p_size <= 0*/ p_size == 0) /*!!! the type of p_size is changed to unsigned size_t*/
        *pos = 0;
    else if (p_size > w_size) {
        *pos = 0; *fpos = 0;
    } else {
        if (p_size < asize) {
            pcrc = IMB_compute_crc((char*)part, p_size);

            *pos = 0;
            wcrc = pcrc - 1;

            while (*pos <= w_size - p_size && wcrc != pcrc) {
                void* h;
                h = (void*)(((char*)whole) + *pos);
                wcrc = IMB_compute_crc((char*)h, p_size);

                if (wcrc != pcrc) (*pos)++;
            }

            if (*pos <= w_size - p_size)
                *D = 0.;
            else {
                *pos  = 0;
                *fpos = 0;
                *D    = 1.;
            }
        } else { /*if( p_size < asize )*/
            *pos = 0;
            w_len = w_size / asize;
            p_len = p_size / asize;

            while (*pos <= w_len - p_len && A_ABS(a_part[0] - a_whole[*pos]) > TOL)
                (*pos)++;

            if (*pos <= w_len - p_len)
                *D = IMB_ddiff(a_part, a_whole + *pos, p_len, fpos);
            else {
                *D = 1.;
                *pos = 0;
                *fpos = 0;
            }

            *pos *= asize;
        } /*if !( p_size < asize )*/
    }

    if (*fpos != CHK_NO_FAULT /*>= 0*/)
        *D = 1.;
}



/********************* CHECKSUM ********************/

/* Most of following CRC-32 stuff is from zmodem source code */

/* I claim no copyright over the contents of this file.  -- Rahul Dhesi */

/*
Checksum:  951252172      (check or update this with "brik")
*/
#define INITCRC 0xFFFFFFFFL
/*
 * Copyright (C) 1986 Gary S. Brown.  You may use this program, or
 * code or tables extracted from it, as desired without restriction.
 */

/* First, the polynomial itself and its table of feedback terms.  The  */
/* polynomial is                                                       */
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
/* Note that we take it "backwards" and put the highest-order term in  */
/* the lowest-order bit.  The X^32 term is "implied"; the LSB is the   */
/* X^31 term, etc.  The X^0 term (usually shown as "+1") results in    */
/* the MSB being 1.                                                    */

/* Note that the usual hardware shift register implementation, which   */
/* is what we're using (we're merely optimizing it by doing eight-bit  */
/* chunks at a time) shifts bits into the lowest-order term.  In our   */
/* implementation, that means shifting towards the right.  Why do we   */
/* do it this way?  Because the calculated CRC must be transmitted in  */
/* order from highest-order term to lowest-order term.  UARTs transmit */
/* characters in order from LSB to MSB.  By storing the CRC this way,  */
/* we hand it to the UART in the order low-byte to high-byte; the UART */
/* sends each low-bit to hight-bit; and the result is transmission bit */
/* by bit from highest- to lowest-order term without requiring any bit */
/* shuffling on our part.  Reception works similarly.                  */

/* The feedback terms table consists of 256, 32-bit entries.  Notes:   */
/*                                                                     */
/*     The table can be generated at runtime if desired; code to do so */
/*     is shown later.  It might not be obvious, but the feedback      */
/*     terms simply represent the results of eight shift/xor opera-    */
/*     tions for all combinations of data and CRC register values.     */
/*                                                                     */
/*     The values must be right-shifted by eight bits by the "updcrc"  */
/*     logic; the shift must be unsigned (bring in zeroes).  On some   */
/*     hardware you could probably optimize the shift in assembler by  */
/*     using byte-swap instructions.                                   */


static long crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};



long IMB_compute_crc (char* buf, size_t size) {
/*

In/out variables:

-buf                  (type char*)
-size                 (type int)

Return value          (type long)

*/
    long crccode = INITCRC;

    if ( /*size <= 0*/ size == 0)   /*!!! the type of size is modified to unsigned size_t*/
        crccode = 0;
    else {
        int i;
        for (i = 0; i < size; i++)
            crccode = crc_32_tab[(int)((crccode) ^ (buf[i])) & 0xff] ^
                      (((crccode) >> 8) & 0x00FFFFFFL);
    }

    return(crccode);
}

#endif
