/*
 * International Chemical Identifier (InChI)
 * Version 1
 * Software version 1.07
 * April 30, 2024
 *
 * MIT License
 *
 * Copyright (c) 2024 IUPAC and InChI Trust
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
*
* The InChI library and programs are free software developed under the
 * auspices of the International Union of Pure and Applied Chemistry (IUPAC).
 * Originally developed at NIST.
 * Modifications and additions by IUPAC and the InChI Trust.
 * Some portions of code were developed/changed by external contributors
 * (either contractor or volunteer) which are listed in the file
 * 'External-contributors' included in this distribution.
 *
 * info@inchi-trust.org
 *
*/

#include <stdlib.h>
#include <string.h>
#include <math.h>

/* */

/* #define CHECK_WIN32_VC_HEAP */

#include "mode.h"

#if ( READ_INCHI_STRING == 1 )

#include "ichitime.h"

/* reverse InChI */

#include "ichitime.h"
#include "ichicant.h"
#include "ichirvrs.h"
#include "inchicmp.h"

#include "inchi_gui.h"

#include "bcf_s.h"

#if 0
#ifndef COMPILE_ANSI_ONLY
#ifndef TARGET_LIB_FOR_WINCHI
#include "inchi_gui.h"
#endif
#endif
#endif


#ifdef TARGET_LIB_FOR_WINCHI
/* dummies */
/****************************************************************************/
int DisplayStructure(struct tagCANON_GLOBALS* pCG,
    inp_ATOM* at,
    int                     num_at,
    OAD_Polymer* polymer,
    int                     num_removed_H,
    int                     bAdd_DT_to_num_H,
    int                     nNumRemovedProtons,
    NUM_H* nNumRemovedProtonsIsotopic,
    int                     bIsotopic,
    int                     j /*bTautomeric*/,
    INChI** cur_INChI,
    INChI_Aux** cur_INChI_Aux,
    int                     bAbcNumbers,
    DRAW_PARMS* dp,
    INCHI_MODE              nMode,
    char* szTitle)
{
    return 0;
}
#endif /* TARGET_LIB_FOR_WINCHI */


/****************************************************************************/
int InChI2Atom(INCHI_CLOCK* ic,
    CANON_GLOBALS* pCG,
    ICHICONST INPUT_PARMS* ip,
    STRUCT_DATA* sd,
    const char* szCurHdr,
    long num_inp,
    StrFromINChI* pStruct,
    int iComponent,
    int iAtNoOffset,
    int  bI2A_Flag,
    int bHasSomeFixedH,
    InpInChI* OneInput)
{
    int ret = 0;

    int iINChI = (bI2A_Flag & I2A_FLAG_RECMET) ? INCHI_REC : INCHI_BAS;
    int bMobileH = (bI2A_Flag & I2A_FLAG_FIXEDH) ? TAUT_NON : TAUT_YES;

    INChI* pInChI[TAUT_NUM];



    memset(pInChI, 0, sizeof(pInChI)); /* djb-rwth: memset_s C11/Annex K variant? */


    /* disconnected or reconnected */

    if (iINChI == INCHI_REC)
    {
        if (!OneInput->nNumComponents[iINChI][TAUT_YES])
        {
            iINChI = INCHI_BAS;
        }
    }
    if (iComponent >= OneInput->nNumComponents[iINChI][TAUT_YES])
    {
        return 0; /* component does not exist */
    }

    /* mobile or fixed H */
    pStruct->bFixedHExists = 0;
    if (bMobileH == TAUT_NON)
    {
        if (!OneInput->nNumComponents[iINChI][bMobileH])
        {
            /* only one InChI exists (no mobile H) */
            bMobileH = TAUT_YES;
        }
    }
    if (iComponent >= OneInput->nNumComponents[iINChI][bMobileH])
    {
        return 0; /* component does not exist */
    }

    /* pointer to the InChI that is going to be reversed */
    pInChI[0] = &OneInput->pInpInChI[iINChI][bMobileH][iComponent];
    pStruct->bMobileH = bMobileH;
    pStruct->iINCHI = iINChI;

    /* deleted component only in case Mobile-H and compound contains only protons */
    if (pInChI[0]->bDeleted)
    {
        return 0; /* deleted component, presumably H(+) */
    }

    if (bMobileH == TAUT_NON && OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons)
    {
        pStruct->nNumRemovedProtonsMobHInChI =
            OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons[iComponent].nNumRemovedProtons;
    }

    if (bMobileH == TAUT_NON ||
        (bMobileH == TAUT_YES &&
            OneInput->pInpInChI[iINChI][TAUT_NON] &&
            OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].nNumberOfAtoms > 0 &&
            !OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].bDeleted) /* djb-rwth: addressing LLVM warning */
        )
    {
        pStruct->bFixedHExists = 1;
    }

    if (bMobileH == TAUT_NON &&
        iComponent < OneInput->nNumComponents[iINChI][TAUT_YES] &&
        OneInput->pInpInChI[iINChI][TAUT_YES] &&
        OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].nNumberOfAtoms > 0 &&
        !OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].bDeleted
        )
    {
        /* pointer to the Mobile-H InChI if we are reversing Fixed-H InChI */
        pInChI[1] = &OneInput->pInpInChI[iINChI][TAUT_YES][iComponent];
    }

    pStruct->num_inp_actual = OneInput->num_inp;

    /* Intercept and correct non-polymer Zz to Zy if applicable */
    if (OneInput->polymer)
    {
        int a, k, new_num;
        OAD_Polymer* p = OneInput->polymer;
        pStruct->n_pzz = 0;
        pStruct->n_zy = 0;
        for (a = 0; a < pInChI[0]->nNumberOfAtoms; a++)
        {
            int aglob = iAtNoOffset + a + 1;
            if (pInChI[0]->nAtom[a] == EL_NUMBER_ZZ)
            {
                new_num = EL_NUMBER_ZY; /* Zy */
                for (k = 0; k < p->n; k++)
                {
                    if ((aglob == p->units[k]->cap1) || (aglob == p->units[k]->cap2))
                    {
                        new_num = EL_NUMBER_ZZ;
                        break;
                    }
                }
                pInChI[0]->nAtom[a] = new_num;
                if (new_num == EL_NUMBER_ZY)
                {
                    pStruct->n_zy++;
                }
                else if (new_num == EL_NUMBER_ZZ)
                {
                    pStruct->n_pzz++;
                }
            }
        }
    }

    ret = OneInChI2Atom(ic, pCG, ip, sd, szCurHdr, num_inp, pStruct,
        iComponent, 0 /* iAtNoOffset*/, bHasSomeFixedH, pInChI);

    /* djb-rwth: fixing oss-fuzz issue #66758, #30283 */
    if (pStruct->at && pStruct->at2)
    {
        int a;
        for (a = 0; a < pInChI[0]->nNumberOfAtoms; a++)
        {
            if (pInChI[0]->nAtom[a] == EL_NUMBER_ZY)
            {
                pInChI[0]->nAtom[a] = EL_NUMBER_ZZ;
                pStruct->at[a].el_number = EL_NUMBER_ZZ;
                strcpy(pStruct->at[a].elname, "Zz");
                pStruct->at2[a].el_number = EL_NUMBER_ZZ;
                strcpy(pStruct->at2[a].elname, "Zz");
                pStruct->n_zy--;
                pStruct->n_pzz++;
            }
        }
    }

    return ret; /* same interpretation as in ProcessOneStructure ??? */
}


/****************************************************************************/
void RemoveFixHInChIIdentical2MobH(InpInChI* pOneInput)
{
    int iInchiRec, cur_num_comp, k;

    /* eliminate Fixed-H InChI that are exactly same as the corresponding Mobile-H structures */
    for (iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec++)
    {
        cur_num_comp = inchi_min(pOneInput->nNumComponents[iInchiRec][TAUT_YES],
            pOneInput->nNumComponents[iInchiRec][TAUT_NON]);
        for (k = 0; k < cur_num_comp; k++)
        {
            if (!CompareReversedINChI(pOneInput->pInpInChI[iInchiRec][TAUT_YES] + k,
                pOneInput->pInpInChI[iInchiRec][TAUT_NON] + k, NULL, NULL))
            {
                Free_INChI_Members(pOneInput->pInpInChI[iInchiRec][TAUT_NON] + k);
                memset(pOneInput->pInpInChI[iInchiRec][TAUT_NON] + k, 0, sizeof(pOneInput->pInpInChI[0][0][0])); /* djb-rwth: memset_s C11/Annex K variant? */
            }
        }
    }
}


/****************************************************************************
 mark Disconnected InChI components that are exactly same as Reconnected ones
 Disconnected will have a negative number of the reconnected component
 Reconnected will have a positive number of the disconnected component
****************************************************************************/
int MarkDisconectedIdenticalToReconnected(InpInChI* pOneInput)
{
    int k1, k2, num_marked = 0;
    int k1max = inchi_max(pOneInput->nNumComponents[INCHI_BAS][TAUT_YES],
        pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]);

    for (k1 = 0; k1 < k1max; k1++)
    {
        int k2max = inchi_max(pOneInput->nNumComponents[INCHI_REC][TAUT_YES],
            pOneInput->nNumComponents[INCHI_REC][TAUT_NON]);

        for (k2 = 0; k2 < k2max; k2++)
        {
            int eqM = k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_YES]
                &&
                k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_YES]
                &&
                !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink /* already linked */
                &&
                !pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].bDeleted
                &&
                pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms
                &&
                pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms ==
                pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nNumberOfAtoms
                &&
                !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].bDeleted
                &&
                !CompareReversedINChI(pOneInput->pInpInChI[INCHI_REC][TAUT_YES] + k2,
                    pOneInput->pInpInChI[INCHI_BAS][TAUT_YES] + k1,
                    NULL, NULL);

            int isF1 = k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]
                &&
                0 == pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].bDeleted
                &&
                0 < pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms
                ;

            int isF2 = k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_NON]
                &&
                0 == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].bDeleted
                &&
                0 < pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms
                ;

            int eqF = isF1
                &&
                isF2
                &&
                !pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink
                &&
                pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms ==
                pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms
                &&
                !CompareReversedINChI(pOneInput->pInpInChI[INCHI_REC][TAUT_NON] + k2,
                    pOneInput->pInpInChI[INCHI_BAS][TAUT_NON] + k1,
                    NULL, NULL)
                ;

            if (eqM && ((!isF1 && !isF2) || eqF)) /* djb-rwth: addressing LLVM warning */
            {
                pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nLink = -(k2 + 1);
                pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink = (k1 + 1);
                if (eqF)
                {
                    pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nLink = -(k2 + 1);
                    pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink = (k1 + 1);
                }
                num_marked++;
                break;
                /* equal InChI has been deleted from the disconnected layer, get next k1 */
            }
        }
    }

    return num_marked;
}


/****************************************************************************/
void SetUpSrm(SRM* pSrm)
{
    /* structure restore parms !!!!! */
    memset(pSrm, 0, sizeof(pSrm[0])); /* djb-rwth: memset_s C11/Annex K variant? */
    pSrm->bFixStereoBonds = FIX_STEREO_BOND_ORDER;
    pSrm->nMetal2EndpointMinBondOrder = 1;
    pSrm->nMetal2EndpointInitEdgeFlow = 0;

    if (METAL_FREE_CHARGE_VAL == 1)
    {
        pSrm->bMetalAddFlower = 1;
        /* the next 3 parameters: */
        /* 0, 0, 0 => all bonds 0, no init radical on metal */
        /* 0, 0, 1 => all bonds 0,    init radical on metal */
        /* 0, 1, 0 => wrong */
        /* 0, 1, 1 => all bonds 1, no init radical on metal */
        /* 1, 0, 1 => min bond order 1, all bonds to metal have order 1 */
        /* 1, 1, 0 => wrong */
        /* 1, 1, 1 => wrong */
        pSrm->nMetalMinBondOrder = 0;
        pSrm->nMetalInitEdgeFlow = 1;
        pSrm->nMetalInitBondOrder = 1;
        pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds;
        pSrm->nMetalFlowerParam_D = 16;
        pSrm->nMetalMaxCharge_D = 16;
    }
    else
    {
        pSrm->bMetalAddFlower = 0;
        pSrm->nMetalMinBondOrder = 1;
        pSrm->nMetalInitEdgeFlow = 0;
        pSrm->nMetalInitBondOrder = 1;
        pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds;
        pSrm->nMetalFlowerParam_D = 16;
        pSrm->nMetalMaxCharge_D = 0;
    }
    pSrm->nMetal2EndpointInitBondOrder = pSrm->nMetal2EndpointMinBondOrder
        + pSrm->nMetal2EndpointInitEdgeFlow;
}


/****************************************************************************/
int MergeStructureComponents(ICHICONST INPUT_PARMS* ip,
    STRUCT_DATA* sd,
    long num_inp,
    char* szCurHdr,
    ICHICONST SRM* pSrm,
    int bReqNonTaut,
    StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    InpInChI* pOneInput)
{
    int iInchiRec, iMobileH, iAlternH, num_components, tot_just_atoms, tot_atoms, cur_nA, cur_nH; /* djb-rwth: removing redundant variables */
    int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp;
    int* nAtomOffs = NULL, * nDelHOffs = NULL;
    StrFromINChI* pStruct1;
    inp_ATOM* at = NULL, * a;

    ret = 0;
    pOneInput->num_atoms = 0;
    /* select highest detail level */
    if ((num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON])) /* djb-rwth: addressing LLVM warning */
    {
        iInchiRec = INCHI_REC;
        iMobileH = TAUT_NON;
    }
    else
    {
        if ((num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES])) /* djb-rwth: addressing LLVM warning */
        {
            iInchiRec = INCHI_REC;
            iMobileH = TAUT_YES;
        }
        else
        {
            if ((num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON])) /* djb-rwth: addressing LLVM warning */
            {
                iInchiRec = INCHI_BAS;
                iMobileH = TAUT_NON;
            }
            else
            {
                if ((num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES])) /* djb-rwth: addressing LLVM warning */
                {
                    iInchiRec = INCHI_BAS;
                    iMobileH = TAUT_YES;
                }
                else
                {
                    return 0; /* no components available */
                }
            }
        }
    }

    nAtomOffs = (int*)inchi_malloc(((long long)num_components + 1) * sizeof(nAtomOffs[0])); /* djb-rwth: cast operator added */
    nDelHOffs = (int*)inchi_malloc(((long long)num_components + 1) * sizeof(nDelHOffs[0])); /* djb-rwth: cast operator added */
    if (!nAtomOffs || !nDelHOffs)
    {
        ret = RI_ERR_ALLOC;
        goto exit_function;
    }
    /* count number of atoms and removed H */
    /* djb-rwth: removing redundant code */
    iAlternH = (iMobileH == TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES]) ? TAUT_YES : -1;
    nAtomOffs[0] = nDelHOffs[0] = 0;
    for (k = 0; k < num_components; k++)
    {
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms ? pStruct[iInchiRec][iMobileH] + k :
            iAlternH >= 0 &&
            pStruct[iInchiRec][iAlternH][k].num_atoms ? pStruct[iInchiRec][iAlternH] + k : NULL;
        if (!pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms || pStruct1->bDeleted)
        {
            cur_nA = cur_nH = 0;
        }
        else
        {
            cur_nA = pStruct1->num_atoms;
            cur_nH = pStruct1->num_deleted_H;
        }
        nAtomOffs[k + 1] = nAtomOffs[k] + cur_nA;
        nDelHOffs[k + 1] = nDelHOffs[k] + cur_nH;
    }
    tot_just_atoms = nAtomOffs[num_components];
    /* shift all H to the end */
    for (k = 0; k <= num_components; k++)
    {
        nDelHOffs[k] += tot_just_atoms;
    }
    tot_atoms = nDelHOffs[num_components];

    /* merge atoms together: 1. Allocate */
    at = (inp_ATOM*)inchi_malloc(((long long)tot_atoms + 1) * sizeof(at[0])); /* djb-rwth: cast operator added */
    if (NULL == at)
    {
        ret = RI_ERR_ALLOC;
        goto exit_function;
    }
    if (!tot_atoms)
    {
        ret = 0;
        goto exit_function; /* empty structure */
    }
    /* merge atoms together: 2. Copy */
    for (k = 0; k < num_components; k++)
    {
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms ? pStruct[iInchiRec][iMobileH] + k :
            iAlternH >= 0 &&
            pStruct[iInchiRec][iAlternH][k].num_atoms ? pStruct[iInchiRec][iAlternH] + k : NULL;
        if ((len = nAtomOffs[k + 1] - nAtomOffs[k])) /* djb-rwth: addressing LLVM warning */
        {
            memcpy(at + nAtomOffs[k], pStruct1->at2, len * sizeof(at[0]));
            if ((len2 = nDelHOffs[k + 1] - nDelHOffs[k])) /* djb-rwth: addressing LLVM warning */
            {
                memcpy(at + nDelHOffs[k], pStruct1->at2 + len, len2 * sizeof(at[0]));
            }
        }
    }
    /* merge atoms together: 3. Update atom numbers */
    icomp = 0;
    for (k = 0; k < num_components; k++)
    {
        iCurAtomOffs = nAtomOffs[k];
        iNxtAtomOffs = nAtomOffs[k + 1];
        iCurDelHOffs = nDelHOffs[k];
        iNxtDelHOffs = nDelHOffs[k + 1];
        len = nAtomOffs[k + 1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */
        iShiftH = iCurDelHOffs - len;
        if (!len)
        {
            continue;
        }
        icomp++; /* current component number */
        /* update atoms */
        for (i = iCurAtomOffs; i < iNxtAtomOffs; i++)
        {

            a = at + i;

            a->endpoint = 0;
            a->bAmbiguousStereo = 0;
            a->at_type = 0;
            a->bCutVertex = 0;
            a->bUsed0DParity = 0;
            a->cFlags = 0;
            a->nBlockSystem = 0;
            a->nNumAtInRingSystem = 0;
            a->nRingSystem = 0;

            for (j = 0; j < a->valence; j++)
            {
                if (a->neighbor[j] < len)
                {
                    a->neighbor[j] += iCurAtomOffs; /* atom */
                }
                else
                {
                    a->neighbor[j] += iShiftH;      /* explicit H */
                }
            }
            a->orig_at_number += iCurAtomOffs;
            a->component = icomp;
            if (a->p_parity)
            {
                for (j = 0; j < MAX_NUM_STEREO_ATOM_NEIGH; j++)
                {
                    if (a->p_orig_at_num[j] <= len)
                    {
                        /* originally, orig_at_num = atom_index+1, therefore <= instead of < */
                        a->p_orig_at_num[j] += iCurAtomOffs;
                    }
                    else
                    {
                        a->p_orig_at_num[j] += iShiftH;
                    }
                }
            }
            for (j = 0; j < MAX_NUM_STEREO_BONDS && a->sb_parity[j]; j++)
            {
                if (a->sn_orig_at_num[j] <= len)
                {
                    /* originally, orig_at_num = atom_index+1, therefore <= instead of < */
                    a->sn_orig_at_num[j] += iCurAtomOffs;
                }
                else
                {
                    a->sn_orig_at_num[j] += iShiftH;
                }
            }
        }
        /* update fixed-H */
        for (i = iCurDelHOffs; i < iNxtDelHOffs; i++)
        {
            a = at + i;
            a->neighbor[0] += iCurAtomOffs;
            a->orig_at_number += iShiftH;
        }
    }
    /* save the results */
    pOneInput->atom = at;
    pOneInput->num_atoms = tot_atoms;
    at = NULL;

exit_function:
    if (at)        inchi_free(at);  /* in case of failure */
    if (nAtomOffs) inchi_free(nAtomOffs);
    if (nDelHOffs) inchi_free(nDelHOffs);
    return ret;
}


#ifndef COMPILE_ANSI_ONLY


static PER_DRAW_PARMS pdp;


/****************************************************************************/
int DisplayAllRestoredComponents(struct tagCANON_GLOBALS* pCG,
    inp_ATOM* at,
    int num_at,
    const char* szCurHdr)
{
    int    ret; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
    char     szTitle[512];
    DRAW_PARMS dp;
    TBL_DRAW_PARMS tdp;
    if (num_at <= 0)
    {
        return 0;
    }
    memset(&dp, 0, sizeof(dp)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&tdp, 0, sizeof(tdp)); /* djb-rwth: memset_s C11/Annex K variant? */
    /*memset( &pdp, 0, sizeof(pdp) );*/
    dp.sdp.tdp = &tdp;
    dp.pdp = &pdp;
    dp.sdp.nFontSize = -9;
    sprintf(szTitle, "All Components of Restored %s Structure", szCurHdr ? szCurHdr : "(No structure name)");
    ret = DisplayStructure(pCG, at, num_at,
        NULL, /* OAD_Polymer *polymer, */
        0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/,
        0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/,
        1 /*int bIsotopic*/, 0 /*bTautomeric*/,
        NULL /* pINChI */, NULL /* INChI_Aux **cur_INChI_Aux*/,
        0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

    return 0;
}


/****************************************************************************/
int DisplayOneRestoredComponent(struct tagCANON_GLOBALS* pCG,
    StrFromINChI* pStruct,
    inp_ATOM* at,
    int iComponent,
    int nNumComponents,
    int bMobileH,
    const char* szCurHdr)
{
    int    ret, k; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
    int    num_at = pStruct->num_atoms;
    XYZ_COORD* pxyz = pStruct->pXYZ;
    char     szTitle[512];
    DRAW_PARMS dp;
    TBL_DRAW_PARMS tdp;
    int         iInchiRec = pStruct->iInchiRec;
    int         iMobileH = pStruct->iMobileH;
    INChI** pInChI = NULL;
    INChI_Aux** pAux = NULL;
    int         nNumRemovedProtons = pAux ? pAux[iMobileH]->nNumRemovedProtons : 0;
    NUM_H* nNumRemovedProtonsIsotopic = pAux ? pAux[iMobileH]->nNumRemovedIsotopicH : NULL;


    if (num_at <= 0 || !pxyz)
    {
        return 0;
    }
    if (iInchiRec && !pStruct->RevInChI.pINChI_Aux[iInchiRec][0])
    {
        iInchiRec = 0;
    }
    k = iMobileH;
    if (!bRevInchiComponentExists(pStruct, iInchiRec, k, 0))
    {
        k = ALT_TAUT(k);
    }
    pInChI = pStruct->RevInChI.pINChI[iInchiRec][0];
    pAux = pStruct->RevInChI.pINChI_Aux[iInchiRec][0];


    memset(&dp, 0, sizeof(dp)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&tdp, 0, sizeof(tdp)); /* djb-rwth: memset_s C11/Annex K variant? */
    /*memset( &pdp, 0, sizeof(pdp) );*/
    dp.sdp.tdp = &tdp;
    dp.pdp = &pdp;
    dp.sdp.nFontSize = -9;
    sprintf(szTitle, "Restored %s Component %d of %d %c%c",
        szCurHdr ? szCurHdr : "(No structure name)", iComponent + 1, nNumComponents,
        pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F');
    ret = DisplayStructure(pCG, at, num_at,
        NULL, /* OAD_Polymer *polymer, */
        0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/,
        nNumRemovedProtons, /*NULL*/ nNumRemovedProtonsIsotopic,
        1 /*int bIsotopic*/, k,
        pInChI, pAux,
        0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

    return 0;
}


/****************************************************************************/
int DisplayRestoredComponent(struct tagCANON_GLOBALS* pCG,
    StrFromINChI* pStruct,
    int iComponent,
    int iAtNoOffset,
    INChI* pInChI,
    const char* szCurHdr)
{
    int    i, ret; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
    int    num_at = pStruct->num_atoms;
    int    num_deleted_H = pStruct->num_deleted_H;
    inp_ATOM* atom = pStruct->at2;
    XYZ_COORD* pxyz = pStruct->pXYZ;
    inp_ATOM* at = NULL;
    char     szTitle[512];
    DRAW_PARMS dp;
    TBL_DRAW_PARMS tdp;
    if (!atom || num_at <= 0 || !pxyz)
    {
        return 0;
    }
    at = (inp_ATOM*)inchi_calloc((long long)num_at + (long long)num_deleted_H, sizeof(at[0])); /* djb-rwth: cast operator added */
    if (!at)
    {
        return RI_ERR_ALLOC;
    }
    memcpy(at, atom, ((long long)num_at + (long long)num_deleted_H) * sizeof(at[0])); /* djb-rwth: cast operator added */
    for (i = 0; i < num_at; i++)
    {
        at[i].x = pxyz[i].xyz[0];
        at[i].y = pxyz[i].xyz[1];
        at[i].z = pxyz[i].xyz[2];
    }
    memset(&dp, 0, sizeof(dp)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&tdp, 0, sizeof(tdp)); /* djb-rwth: memset_s C11/Annex K variant? */
    /*memset( &pdp, 0, sizeof(pdp) );*/
    dp.sdp.tdp = &tdp;
    dp.pdp = &pdp;
    dp.sdp.nFontSize = -9;
    sprintf(szTitle, "DBG Restored %s Component %d %c%c", szCurHdr ? szCurHdr : "(No structure name)", iComponent + 1, pStruct->iInchiRec ? 'R' : 'D', pStruct->iMobileH ? 'M' : 'F');

    ret = DisplayStructure(pCG, at, num_at,
        NULL, /* OAD_Polymer *polymer, */
        0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/,
        0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/,
        1 /*int bIsotopic*/, 0 /*bTautomeric*/,
        &pInChI, NULL /* INChI_Aux **cur_INChI_Aux*/,
        0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

    inchi_free(at);

    return 0;
}


/****************************************************************************/
int DisplayStructureComponents(struct tagCANON_GLOBALS* pCG,
    ICHICONST INPUT_PARMS* ip,
    STRUCT_DATA* sd,
    long num_inp,
    char* szCurHdr,
    ICHICONST SRM* pSrm,
    int bReqNonTaut,
    StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    InpInChI* pOneInput)
{
    int iInchiRec, iMobileH,
        iCurMobH = -1,
        iAlternH, num_components, tot_just_atoms, tot_atoms, cur_nA, cur_nH; /* djb-rwth: removing redundant variables */
    int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, len, len2; /* djb-rwth: removing redundant variables; removing redundant variables/code */
    int* nAtomOffs = NULL, * nDelHOffs = NULL, iNewCoord = 0, nNewCoord = 0; /* djb-rwth: removing redundant variables */
    double x_max = -1.0e16, x_min = 1.0e16, y_max = -1.0e16, y_min = 1.0e16, delta = 0.0;
    StrFromINChI* pStruct1;
    inp_ATOM* at = NULL, * a;

    if (!ip->bDisplayCompositeResults && !ip->bDisplay)
    {
        return 0;
    }

    ret = 0;
    pOneInput->num_atoms = 0;
    /* select highest detail level */
    if ((num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON])) /* djb-rwth: addressing LLVM warning */
    {
        iInchiRec = INCHI_REC;
        iMobileH = TAUT_NON;
    }
    else
    {
        if ((num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES])) /* djb-rwth: addressing LLVM warning */
        {
            iInchiRec = INCHI_REC;
            iMobileH = TAUT_YES;
        }
        else
        {
            if ((num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON])) /* djb-rwth: addressing LLVM warning */
            {
                iInchiRec = INCHI_BAS;
                iMobileH = TAUT_NON;
            }
            else
            {
                if ((num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES])) /* djb-rwth: addressing LLVM warning */
                {
                    iInchiRec = INCHI_BAS;
                    iMobileH = TAUT_YES;
                }
                else
                {
                    return 0; /* no components available */
                }
            }
        }
    }

    for (k = 0; k < num_components; k++)
    {
        if (pStruct[iInchiRec][iMobileH][k].bDeleted)
        {
            break;
        }
    }
    num_components = k;

    nAtomOffs = (int*)inchi_malloc(((long long)num_components + 1) * sizeof(nAtomOffs[0])); /* djb-rwth: cast operator added */
    nDelHOffs = (int*)inchi_malloc(((long long)num_components + 1) * sizeof(nDelHOffs[0])); /* djb-rwth: cast operator added */
    if (!nAtomOffs || !nDelHOffs)
    {
        ret = RI_ERR_ALLOC;
        goto exit_function;
    }

    /* count number of atoms and removed H */
    /* djb-rwth: removing redundant code */
    iAlternH = (iMobileH == TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES]) ? TAUT_YES : -1;
    nAtomOffs[0] = nDelHOffs[0] = 0;

    for (k = 0; k < num_components; k++)
    {
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms ? pStruct[iInchiRec][iMobileH] + k :
            iAlternH >= 0 &&
            pStruct[iInchiRec][iAlternH][k].num_atoms ? pStruct[iInchiRec][iAlternH] + k : NULL;
        if (!pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms)
        {
            cur_nA = cur_nH = 0;
        }
        else
        {
            cur_nA = pStruct1->num_atoms;
            cur_nH = pStruct1->num_deleted_H;
            if (cur_nA && !pStruct1->pXYZ)
            {
                if (!k)
                {
                    ret = 0; /* no coordinates available */
                    goto exit_function;
                }
                /* djb-rwth: removing redundant code */
            }
        }
        nAtomOffs[k + 1] = nAtomOffs[k] + cur_nA;
        nDelHOffs[k + 1] = nDelHOffs[k] + cur_nH;
    }
    tot_just_atoms = nAtomOffs[num_components];

    /* shift all H to the end */
    for (k = 0; k <= num_components; k++)
    {
        nDelHOffs[k] += tot_just_atoms;
    }
    tot_atoms = nDelHOffs[num_components];

    /* merge atoms together: 1. Allocate */
    if (NULL == (at = (inp_ATOM*)inchi_malloc(((long long)tot_atoms + 1) * sizeof(at[0])))) /* djb-rwth: cast operator added */
    {
        ret = RI_ERR_ALLOC;
        goto exit_function;
    }
    if (!tot_atoms)
    {
        ret = 0;
        goto exit_function; /* empty structure */
    }

    /* merge atoms together: 2. Copy */
    for (k = 0; k < num_components; k++)
    {
        pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms ? pStruct[iInchiRec][iCurMobH = iMobileH] + k :
            iAlternH >= 0 &&
            pStruct[iInchiRec][iAlternH][k].num_atoms ? pStruct[iInchiRec][iCurMobH = iAlternH] + k : NULL;
        if ((len = nAtomOffs[k + 1] - nAtomOffs[k]) && pStruct1) /* djb-rwth: addressing LLVM warning */
        {
            XYZ_COORD* pxyz = pStruct1->pXYZ;
            len2 = nDelHOffs[k + 1] - nDelHOffs[k]; /* do not separate H from the atom: we will not need them */
            iCurAtomOffs = nAtomOffs[k];
            a = at + iCurAtomOffs;
            memcpy(a, pStruct1->at2, ((long long)len + (long long)len2) * sizeof(at[0])); /* djb-rwth: cast operator added */
            DisconnectedConnectedH(a, len, len2);
            if (pxyz)
            {
                for (i = 0; i < len; i++)
                {
                    a[i].x = pxyz[i].xyz[0];
                    x_max = inchi_max(x_max, pxyz[i].xyz[0]);
                    x_min = inchi_min(x_min, pxyz[i].xyz[0]);
                    a[i].y = pxyz[i].xyz[1];
                    y_max = inchi_max(y_max, pxyz[i].xyz[1]);
                    y_min = inchi_min(y_min, pxyz[i].xyz[1]);
                    a[i].z = pxyz[i].xyz[2];
                    nNewCoord++;
                }
            }
            else
            {
                if (!iNewCoord)
                {
                    if (!nNewCoord)
                    {
                        ret = 0;
                        goto exit_function; /* empty structure */
                    }
                    delta = inchi_max(x_max - x_min, y_max - y_min);
                    if (delta == 0.0)
                    {
                        delta = 0.5 * (x_max + x_min);
                        if (delta == 0.0)
                            delta = 1.0;
                    }
                    else
                    {
                        delta /= sqrt((double)((long long)nNewCoord + 1)); /* djb-rwth: cast operator added */
                    }
                }
                for (i = 0; i < len; i++)
                {
                    a[i].x = x_max + delta;
                    a[i].y = y_max - iNewCoord * delta;
                    a[i].z = 0.0;
                    iNewCoord++;
                }
                if ((pStruct1->pXYZ = (XYZ_COORD*)inchi_calloc(len, sizeof(pStruct1->pXYZ[0])))) /* djb-rwth: addressing LLVM warning */
                {

                    for (i = 0; i < len; i++)
                    {
                        pStruct1->pXYZ[i].xyz[0] = a[i].x;
                        pStruct1->pXYZ[i].xyz[1] = a[i].y;
                        pStruct1->pXYZ[i].xyz[2] = 0.0;
                    }
                }
            }
            if (ip->bDisplay || (ip->bDisplayCompositeResults && 1 == num_components)) /* djb-rwth: addressing LLVM warning */
            {
                DisplayOneRestoredComponent(pCG, pStruct1, a, k, num_components, iCurMobH, szCurHdr);
            }
            if (!pxyz && pStruct1->pXYZ)
            {
                inchi_free(pStruct1->pXYZ);
                pStruct1->pXYZ = NULL;
            }
        }
    }

    /* merge atoms together: 3. Update atom numbers */
    /* djb-rwth: removing redundant code */
    if (ip->bDisplayCompositeResults && num_components > 1)
    {
        for (k = 0; k < num_components; k++)
        {
            /* display each restored component if requested */
            iCurAtomOffs = nAtomOffs[k];
            iNxtAtomOffs = nAtomOffs[k + 1];
            /* djb-rwth: removing redundant code */
            /* djb-rwth: removing redundant code */
            len = nAtomOffs[k + 1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */
            /* djb-rwth: removing redundant code */
            if (!len)
            {
                continue;
            }
            /* djb-rwth: removing redundant code */
            /* update atoms */
            for (i = iCurAtomOffs; i < iNxtAtomOffs; i++)
            {
                a = at + i;
                for (j = 0; j < a->valence; j++)
                {
                    if (a->neighbor[j] < len)
                    {
                        a->neighbor[j] += iCurAtomOffs; /* atom */
                    }
                    else
                    {
                        ret = RI_ERR_PROGR;  /* explicit H */
                        goto exit_function;
                    }
                }
                a->orig_at_number += iCurAtomOffs;
            }
        }
        tot_atoms = nAtomOffs[num_components];
        DisplayAllRestoredComponents(pCG, at, tot_atoms, szCurHdr);
    }

exit_function:

    if (at)
    {
        inchi_free(at);  /* in case of failure */
    }
    if (nAtomOffs)
    {
        inchi_free(nAtomOffs);
    }
    if (nDelHOffs)
    {
        inchi_free(nDelHOffs);
    }

    return ret;
}
#endif


/****************************************************************************/
int AllInchiToStructure(INCHI_CLOCK* ic,
    CANON_GLOBALS* pCG,
    ICHICONST INPUT_PARMS* ip_inp,
    STRUCT_DATA* sd_inp,
    long num_inp,
    char* szCurHdr,
    ICHICONST SRM* pSrm,
    int bHasSomeFixedH,
    StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    InpInChI* pOneInput)
{
    int iInchiRec, iMobileH, cur_num_comp, bCurI2A_Flag, k, ret, num_err;
    int iAtNoOffset;
    INPUT_PARMS* ip, ip_loc;
    STRUCT_DATA* sd, sd_loc;
    long          ulProcessingTime = 0;
    inchiTime     ulTStart;

    InchiTimeGet(&ulTStart);
    ip = &ip_loc;
    *ip = *ip_inp;
    sd = &sd_loc;
    memset(sd, 0, sizeof(*sd)); /* djb-rwth: memset_s C11/Annex K variant? */
    sd->ulStructTime = sd_inp->ulStructTime;
    ret = 0;
    num_err = 0;
    for (iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec++)
    {
        /* Disconnected/Connected */
        for (iMobileH = 0; iMobileH < TAUT_NUM; iMobileH++)
        {
            /* Mobile/Fixed H */
            cur_num_comp = pOneInput->nNumComponents[iInchiRec][iMobileH];
            if (!cur_num_comp)
            {
                continue;
            }
            /* allocate memory for all existing components */
            pStruct[iInchiRec][iMobileH] = (StrFromINChI*)inchi_calloc(cur_num_comp, sizeof(pStruct[0][0][0]));
            if (!pStruct[iInchiRec][iMobileH])
            {
                ret = RI_ERR_ALLOC;
                goto exit_error;
            }
            /* set conversion mode */
            bCurI2A_Flag = (iMobileH ? 0 : I2A_FLAG_FIXEDH) | (iInchiRec ? I2A_FLAG_RECMET : 0);
            if (iMobileH)
            {
                ip->nMode &= ~REQ_MODE_BASIC;
            }
            else
            {
                ip->nMode |= REQ_MODE_BASIC;
            }
            /* InChI --> structure conversion for all components except duplicated */
            iAtNoOffset = 0;
            for (k = 0; k < cur_num_comp; k++)
            {
                /* components */
                if ((!iMobileH && !pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms) ||
                    pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted ||
                    pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink < 0) /* djb-rwth: addressing LLVM warning */
                {
                    pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink;
                    pStruct[iInchiRec][iMobileH][k].bDeleted = pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted;
                    continue; /* do not create a structure out of an unavailable
                                 Fixed-H InChI or out of the one present in Reconnected layer */

#ifdef NEVER  /* a wrong attempt to process deleted components here */
                    if (pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink)
                    {
                        continue; /* do not create a structure out of an unavailable
                                     Fixed-H InChI or out of the one present in Reconnected layer */
                    }
                    else
                        if (iMobileH && pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms &&
                            pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted &&
                            pOneInput->pInpInChI[iInchiRec][iMobileH][0].bDeleted)
                        {
                            /* all components are protons */
                            ;
                        }
                        else
                        {
                            continue;
                        }
#endif

                }
                if (bHasSomeFixedH && iMobileH && k < pOneInput->nNumComponents[iInchiRec][TAUT_NON] &&
                    pOneInput->pInpInChI[iInchiRec][TAUT_NON][k].nNumberOfAtoms)
                {
                    continue; /* do not process Mobile-H if Fixed-H is requested and exists */
                }
                pStruct[iInchiRec][iMobileH][k].pSrm = pSrm;
                pStruct[iInchiRec][iMobileH][k].iInchiRec = iInchiRec;
                pStruct[iInchiRec][iMobileH][k].iMobileH = iMobileH;

                /****************************************************/
                /*                                                  */
                /* Convert InChI of one component into a Structure  */
                /*                                                  */
                /****************************************************/

                ret = InChI2Atom(ic, pCG, ip, sd, szCurHdr, num_inp, pStruct[iInchiRec][iMobileH] + k, k,
                    iAtNoOffset /* 0*/, bCurI2A_Flag, bHasSomeFixedH, pOneInput);
                pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink;
                if (ret < 0)
                {
#if ( bRELEASE_VERSION != 1 )
#ifndef TARGET_API_LIB
                    /* !!! Conversion Error -- Ignore for now !!! */
                    fprintf(stdout, "%ld %s Conversion failed: %d, %c%c comp %d\n",
                        num_inp, szCurHdr ? szCurHdr : "Struct", ret, iInchiRec ? 'R' : 'D', iMobileH ? 'M' : 'F', k + 1);
#endif
#endif
                    if (ret == CT_USER_QUIT_ERR)
                    {
                        goto exit_error;
                    }
                    pStruct[iInchiRec][iMobileH][k].nError = ret;
                    ret = 0; /* force to ignore the errors for now !!!! */
                    num_err++;
                }
                iAtNoOffset += pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms;
            } /* k-th component */
        }
    }

exit_error:
    ulProcessingTime += InchiTimeElapsed(ic, &ulTStart);
    sd->ulStructTime += ulProcessingTime;

    return ret < 0 ? ret : num_err;
}


/****************************************************************************/
int AddProtonAndIsoHBalanceToMobHStruct(INCHI_CLOCK* ic,
    CANON_GLOBALS* pCG,
    ICHICONST INPUT_PARMS* ip,
    STRUCT_DATA* sd,
    long num_inp,
    int bHasSomeFixedH,
    char* szCurHdr,
    StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    InpInChI* pOneInput)
{
    COMPONENT_REM_PROTONS nToBeRemovedByNormFromRevrs[INCHI_NUM];
    int                   nRemovedByNormFromRevrs[INCHI_NUM];
    int                   nRemovedByRevrs[INCHI_NUM];

    int   nDeltaFromDisconnected = 0, nRemovedProtonsByNormFromRevrs, nRemovedProtonsByRevrs; /* djb-rwth: removing redundant variables */
    NUM_H nIsoDeltaFromDisconnected[NUM_H_ISOTOPES];
    int iInchiRec, i, k, k1, ret = 0;
    int  nChargeInChI, nChargeRevrs;

    if (bHasSomeFixedH)
    {
        return 0; /* 2005-03-01 */
    }

    /* num protons removed by InChI Normalization from the original structure */
    for (i = 0; i < INCHI_NUM; i++)
    {
        nToBeRemovedByNormFromRevrs[i].nNumRemovedProtons = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedProtons;
        for (k = 0; k < NUM_H_ISOTOPES; k++)
        {
            nToBeRemovedByNormFromRevrs[i].nNumRemovedIsotopicH[k] = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedIsotopicH[k];
        }
    }
    /* accumulate here num. protons removed by the normalization from the reversed structure */
    nRemovedByNormFromRevrs[INCHI_BAS] =
        nRemovedByNormFromRevrs[INCHI_REC] = 0;
    nRemovedByRevrs[INCHI_REC] =
        nRemovedByRevrs[INCHI_BAS] = 0;
    /* protons added/removed by InChI Normalization to/from Restored Structure might have been added by StructureRestore */

    for (iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec++)
    {
        for (k = 0; k < pOneInput->nNumComponents[iInchiRec][TAUT_YES]; k++)
        {
            if (!bInpInchiComponentExists(pOneInput, iInchiRec, TAUT_YES, k))
            {
                continue;
            }
            nRemovedProtonsByNormFromRevrs = 0; /* Num protons removed from the Restored Structure by InChI Normalization */
            nRemovedProtonsByRevrs = 0; /* Num protons removed by the Reconstruction from the Restored Structure */
            if (iInchiRec == INCHI_REC || (iInchiRec == INCHI_BAS && (pStruct[iInchiRec][TAUT_YES][k].nLink) >= 0)) /* djb-rwth: addressing LLVM warning; removing redundant code */
            {

                REV_INCHI* pRevInChI = &pStruct[iInchiRec][TAUT_YES][k].RevInChI;
                INChI_Aux** pINChI_Aux2 = pRevInChI->pINChI_Aux[iInchiRec][0]; /* component 0*/
                INChI** pINChI_Revr = pRevInChI->pINChI[iInchiRec][0];
                INChI* pINChI_Orig = pOneInput->pInpInChI[iInchiRec][TAUT_YES] + k;
                nChargeRevrs = pINChI_Revr ? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT;
                nChargeInChI = pINChI_Orig->nTotalCharge;
                if (pINChI_Aux2)
                {
                    nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons;
                }
                nRemovedProtonsByRevrs = pStruct[iInchiRec][TAUT_YES][k].nNumRemovedProtonsByRevrs;
                pStruct[iInchiRec][TAUT_YES][k].nChargeRevrs = nChargeRevrs;
                pStruct[iInchiRec][TAUT_YES][k].nChargeInChI = nChargeInChI;
            }
            else
            {
                if (0 <= (k1 = -(1 + pStruct[iInchiRec][TAUT_YES][k].nLink)))
                {
                    REV_INCHI* pRevInChI = &pStruct[INCHI_REC][TAUT_YES][k1].RevInChI;
                    INChI_Aux** pINChI_Aux2 = pRevInChI->pINChI_Aux[INCHI_BAS][0]; /* component 0 */
                    INChI** pINChI_Revr = pRevInChI->pINChI[INCHI_BAS][0];
                    INChI* pINChI_Orig = pOneInput->pInpInChI[INCHI_REC][TAUT_YES] + k1;
                    nChargeRevrs = pINChI_Revr ? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT;
                    nChargeInChI = pINChI_Orig->nTotalCharge;
                    if (pINChI_Aux2)
                    {
                        nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons;
                    }
                    /* this component cannot be disconnected because it is same as in reconnected layer */
                    nRemovedProtonsByRevrs = pStruct[INCHI_REC][TAUT_YES][k1].nNumRemovedProtonsByRevrs;
                    pStruct[iInchiRec][TAUT_YES][k1].nChargeRevrs = nChargeRevrs;
                    pStruct[iInchiRec][TAUT_YES][k1].nChargeInChI = nChargeInChI;
                }
            }
            /* how many protons (to be removed by InChI Normalization) to add =
            (proton balance in InChI} -
            {number of protons known to be removed by InChI Normalization from Reconstructed structure} */
            nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons -= nRemovedProtonsByNormFromRevrs;
            nRemovedByNormFromRevrs[iInchiRec] += nRemovedProtonsByNormFromRevrs;
            nRemovedByRevrs[iInchiRec] += nRemovedProtonsByRevrs;
            pStruct[iInchiRec][TAUT_YES][k].nRemovedProtonsByNormFromRevrs = nRemovedProtonsByNormFromRevrs;
        }
    }

    /* Since fixed-H layer is missing we need to add proton balance to the components */
    memset(nIsoDeltaFromDisconnected, 0, sizeof(nIsoDeltaFromDisconnected)); /* djb-rwth: memset_s C11/Annex K variant? */
    for (iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec--)
    {
        /*
        if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] &&
              pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) {
        */
        int bHasRecMobH = (iInchiRec == INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]);
        /* bHasRecMobH means all components that could not be disconnected are in reconnected part */
        if (iInchiRec == INCHI_BAS)
        {
            /* second pass: common structures have been changed */
            nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedProtons += nDeltaFromDisconnected;
        }
        /* after proton removal InChI is recalculated */

        ret = AddRemProtonsInRestrStruct(ic, pCG, ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES],
            pOneInput->nNumComponents[iInchiRec][TAUT_YES],
            bHasRecMobH ? pStruct[INCHI_REC][TAUT_YES] : NULL,
            bHasRecMobH ? pOneInput->nNumComponents[INCHI_REC][TAUT_YES] : 0,
            &nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons,
            (iInchiRec == INCHI_REC) ? &nDeltaFromDisconnected : NULL);
        if (ret < 0)
        {
            goto exit_function;
        }
        /* djb-rwth: removing redundant code */
       /*
       }
       */
    }

    /* if fixed-H layer is missing then we need to add isotopic exchangeable proton balance to the components */
    for (iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec--)
    {
        /*
        if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] &&
              pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) {
        */
        int bHasRecMobH = (iInchiRec == INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]);
        /* bHasRecMobH means all components that could not be disconnected are in reconnected part */
        if (iInchiRec == INCHI_BAS)
        {
            /* second pass: common structures have been changed */
            for (k = 0; k < NUM_H_ISOTOPES; k++)
            {
                nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedIsotopicH[k] += nIsoDeltaFromDisconnected[k];
            }
        }

        /* after proton removal InChI is recalculated */
        ret = AddRemIsoProtonsInRestrStruct(ic, pCG, ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES],
            pOneInput->nNumComponents[iInchiRec][TAUT_YES],
            bHasRecMobH ? pStruct[INCHI_REC][TAUT_YES] : NULL,
            bHasRecMobH ? pOneInput->nNumComponents[INCHI_REC][TAUT_YES] : 0,
            nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedIsotopicH,
            (iInchiRec == INCHI_REC) ? nIsoDeltaFromDisconnected : NULL);

        if (ret < 0)
        {
            goto exit_function;
        }
        /* djb-rwth: removing redundant code */
        /*
        }
        */
    }

exit_function:

    return ret;
}


/****************************************************************************/
void FreeStrFromINChI(StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    int nNumComponents[INCHI_NUM][TAUT_NUM])
{
    int iInchiRec, iMobileH, cur_num_comp, k, j;
    StrFromINChI* pStruct1;
    for (iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec++)
    {
        for (iMobileH = 0; iMobileH < TAUT_NUM; iMobileH++)
        {
            cur_num_comp = nNumComponents[iInchiRec][iMobileH];
            if (!cur_num_comp || !(pStruct1 = pStruct[iInchiRec][iMobileH]))
            {
                continue;
            }
            for (k = 0; k < cur_num_comp; k++)
            {
                if (pStruct1[k].at)
                {
                    inchi_free(pStruct1[k].at);
                }
                if (pStruct1[k].at2)
                {
                    inchi_free(pStruct1[k].at2);
                }
                if (pStruct1[k].st)
                {
                    inchi_free(pStruct1[k].st);
                }
                if (pStruct1[k].pVA)
                {
                    inchi_free(pStruct1[k].pVA);
                }
                /*
                if ( pStruct1[k].ti.t_group ) {
                    inchi_free( pStruct1[k].ti.t_group );
                }
                */
                if ( pStruct1[k].One_ti.t_group ) {
                    inchi_free( pStruct1[k].One_ti.t_group );
                }
                if (pStruct1[k].pXYZ)
                {
                    inchi_free(pStruct1[k].pXYZ); /* djb-rwth: ui_rr? */
                }
                /*==== begin ====*/
                free_t_group_info(&pStruct1[k].ti);
                if (pStruct1[k].endpoint)
                {
                    inchi_free(pStruct1[k].endpoint); /* djb-rwth: ui_rr? */
                }
                if (pStruct1[k].fixed_H)
                {
                    inchi_free(pStruct1[k].fixed_H); /* djb-rwth: ui_rr? */
                }
                for (j = 0; j < TAUT_NUM; j++)
                {
                    if (pStruct1[k].nAtno2Canon[j])
                        inchi_free(pStruct1[k].nAtno2Canon[j]);
                    if (pStruct1[k].nCanon2Atno[j])
                        inchi_free(pStruct1[k].nCanon2Atno[j]);
                }
                /*===== end ======*/
                /*  free INChI memory */
                FreeAllINChIArrays(pStruct1[k].RevInChI.pINChI,
                    pStruct1[k].RevInChI.pINChI_Aux,
                    pStruct1[k].RevInChI.num_components);
#ifdef NEVER
                /* don't do that: these are just pointers to OneInput structure members */
                Free_INChI(&pStruct1[k].pINChI);
                Free_INChI_Aux(&pStruct1[k].pINChI_Aux);
                if (pStruct1[k].inp_norm_data)
                {
                    FreeInpAtomData(pStruct1[k].inp_norm_data);
                    inchi_free(pStruct1[k].inp_norm_data);
                }
#endif
            }
            inchi_free(pStruct[iInchiRec][iMobileH]);
            pStruct[iInchiRec][iMobileH] = NULL;
        }
    }
}


/****************************************************************************/
void FreeInpInChI(InpInChI* pOneInput)
{
    int iINChI, k, j;
    for (iINChI = 0; iINChI < INCHI_NUM; iINChI++)
    {
        for (j = 0; j < TAUT_NUM; j++)
        {
            if (pOneInput->pInpInChI[iINChI][j])
            {
                for (k = 0; k < pOneInput->nNumComponents[iINChI][j]; k++)
                {
#if (FIX_OSS_FUZZ_25734_28139 == 1)
                    U_CHAR* k_nAtom = (&pOneInput->pInpInChI[iINChI][j][k])->nAtom;
                    AT_NUMB* k_nConnTable = (&pOneInput->pInpInChI[iINChI][j][k])->nConnTable;
                    AT_NUMB* k_nTautomer = (&pOneInput->pInpInChI[iINChI][j][k])->nTautomer;
                    S_CHAR* k_nNum_H = (&pOneInput->pInpInChI[iINChI][j][k])->nNum_H;
                    S_CHAR* k_nNum_H_fixed = (&pOneInput->pInpInChI[iINChI][j][k])->nNum_H_fixed;
                    char* k_szHillFormula = (&pOneInput->pInpInChI[iINChI][j][k])->szHillFormula;
                    AT_NUMB* k_nPossibleLocationsOfIsotopicH = (&pOneInput->pInpInChI[iINChI][j][k])->nPossibleLocationsOfIsotopicH;
                    INChI_IsotopicAtom* k_IsotopicAtom = (&pOneInput->pInpInChI[iINChI][j][k])->IsotopicAtom;
                    INChI_IsotopicTGroup* k_IsotopicTGroup = (&pOneInput->pInpInChI[iINChI][j][k])->IsotopicTGroup;
                    INChI_Stereo* k_Stereo = (&pOneInput->pInpInChI[iINChI][j][k])->Stereo;
                    INChI_Stereo* k_StereoIsotopic = (&pOneInput->pInpInChI[iINChI][j][k])->StereoIsotopic;

#endif

                    Free_INChI_Members(&pOneInput->pInpInChI[iINChI][j][k]);

#if (FIX_OSS_FUZZ_25734_28139 == 1)
                    {
                        /* prevent erroneous repeated freeing in copied pInpInChIp[][][kk] */
                        int kk;
                        for (kk = k + 1; kk < pOneInput->nNumComponents[iINChI][j]; kk++)
                        {
                            if (k_nAtom == (&pOneInput->pInpInChI[iINChI][j][kk])->nAtom)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->nAtom = NULL;
                            }
                            if (k_nConnTable == (&pOneInput->pInpInChI[iINChI][j][kk])->nConnTable)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->nConnTable = NULL;
                            }
                            if (k_nTautomer == (&pOneInput->pInpInChI[iINChI][j][kk])->nTautomer)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->nTautomer = NULL;
                            }
                            if (k_nNum_H == (&pOneInput->pInpInChI[iINChI][j][kk])->nNum_H)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->nNum_H = NULL;
                            }
                            if (k_nNum_H_fixed == (&pOneInput->pInpInChI[iINChI][j][kk])->nNum_H_fixed)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->nNum_H_fixed = NULL;
                            }
                            if (k_szHillFormula == (&pOneInput->pInpInChI[iINChI][j][kk])->szHillFormula)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->szHillFormula = NULL;
                            }
                            if (k_nPossibleLocationsOfIsotopicH == (&pOneInput->pInpInChI[iINChI][j][kk])->nPossibleLocationsOfIsotopicH)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->nPossibleLocationsOfIsotopicH = NULL;
                            }

                            if (k_IsotopicAtom == (&pOneInput->pInpInChI[iINChI][j][kk])->IsotopicAtom)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->IsotopicAtom = NULL;
                            }

                            if (k_IsotopicTGroup == (&pOneInput->pInpInChI[iINChI][j][kk])->IsotopicTGroup)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->IsotopicTGroup = NULL;
                            }

                            if (k_Stereo == (&pOneInput->pInpInChI[iINChI][j][kk])->Stereo)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->Stereo = NULL;
                            }

                            if (k_StereoIsotopic == (&pOneInput->pInpInChI[iINChI][j][kk])->StereoIsotopic)
                            {
                                (&pOneInput->pInpInChI[iINChI][j][kk])->StereoIsotopic = NULL;
                            }

                        }
                    }
#endif

                }
                inchi_free(pOneInput->pInpInChI[iINChI][j]);
                pOneInput->pInpInChI[iINChI][j] = NULL;
            }
            if (pOneInput->nNumProtons[iINChI][j].pNumProtons)
            {
                inchi_free(pOneInput->nNumProtons[iINChI][j].pNumProtons);
                pOneInput->nNumProtons[iINChI][j].pNumProtons = NULL;
            }
        }
    }
    if (pOneInput->atom)
    {
        inchi_free(pOneInput->atom);
    }

    FreeExtOrigAtData(pOneInput->polymer, pOneInput->v3000);

    memset(pOneInput, 0, sizeof(*pOneInput)); /* djb-rwth: memset_s C11/Annex K variant? */
}


/****************************************************************************/
int CompareAllOrigInchiToRevInChI(StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    InpInChI* pOneInput,
    int bReqNonTaut,
    long num_inp,
    char* szCurHdr)
{
    int i, iInchiRec, iMobileH, iMobileHpStruct, num_components, iComponent, ret = 0; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
    COMPONENT_REM_PROTONS nCurRemovedProtons, nNumRemovedProtons;
    INChI* pInChI[TAUT_NUM];
    INCHI_MODE  CompareInchiFlags[TAUT_NUM];
    memset(pOneInput->CompareInchiFlags[0], 0, sizeof(pOneInput->CompareInchiFlags[0])); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&nNumRemovedProtons, 0, sizeof(nNumRemovedProtons)); /* djb-rwth: memset_s C11/Annex K variant? */

    /* do we have reconnected InChI ?*/
    iInchiRec = INCHI_REC;
    iMobileH = TAUT_NON;
    if (!pOneInput->nNumComponents[iInchiRec][TAUT_YES] && !pOneInput->nNumComponents[iInchiRec][TAUT_NON])
    {
        iInchiRec = INCHI_BAS;
    }
    /* do we have Mobile or Fixed-H ? */
    if (!pOneInput->nNumComponents[iInchiRec][TAUT_NON] || !bReqNonTaut)
    {
        iMobileH = TAUT_YES;  /* index for pOneInput */
    }
    /* if a restored structure has Fixed-H InChI then its mobile-H restored InChI is in Fixed-H pStruct */
    num_components = pOneInput->nNumComponents[iInchiRec][iMobileH];
    for (iComponent = 0; iComponent < num_components; iComponent++)
    {
        int bMobileH = iMobileH;
        pInChI[0] = pInChI[1] = NULL;
        if (pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].nNumberOfAtoms &&
            !pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].bDeleted)
        {
            /* the requested InChI layer exists */
            pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent];
            if (bMobileH == TAUT_NON)
            {
                pInChI[1] = &pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent];
            }
        }
        else
        {
            if (bMobileH == TAUT_NON &&
                pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].nNumberOfAtoms &&
                !pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].bDeleted)
            {
                /* the requested Fixed-H InChI layer does not exist; however, the Mobile-H does exist */
                bMobileH = TAUT_YES; /* only Mobile-H is available */
                pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent];
            }
        }
        memset(CompareInchiFlags, 0, sizeof(CompareInchiFlags)); /* djb-rwth: memset_s C11/Annex K variant? */
        memset(&nCurRemovedProtons, 0, sizeof(nCurRemovedProtons)); /* djb-rwth: memset_s C11/Annex K variant? */
        iMobileHpStruct =
#if ( bRELEASE_VERSION == 0 )
#ifndef TARGET_API_LIB
            /* legacy: reproduce old output */
            OldPrintCompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH] + iComponent, pInChI, bMobileH,
                iComponent, num_inp, szCurHdr);
#endif
#endif
        /* one component comparison result bits */
        ret = CompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH] + iComponent, pInChI, bMobileH, iComponent,
            num_inp, szCurHdr, &nCurRemovedProtons, CompareInchiFlags);
        if (ret >= 0)
        {
            /* no errors encountered -> accumulate removed protons from individual Mobile-H layers of components */
            nNumRemovedProtons.nNumRemovedProtons += nCurRemovedProtons.nNumRemovedProtons;
            for (i = 0; i < NUM_H_ISOTOPES; i++)
            {
                nNumRemovedProtons.nNumRemovedIsotopicH[i] += nCurRemovedProtons.nNumRemovedIsotopicH[i];
            }
            /* accumulate compare bits */
            for (i = 0; i < TAUT_NUM; i++)
            {
                pOneInput->CompareInchiFlags[0][i] |= CompareInchiFlags[i];
            }
        }
        else
        {
            goto exit_function;
        }
    }
    if (iMobileH == TAUT_YES)
    {
        if (pOneInput->nNumProtons[iInchiRec][iMobileH].pNumProtons)
        {
            ret = RI_ERR_PROGR; /* in Mobile-H case proton balances are split between compoments */
        }
        else
        {
            /*   num removed protons in orig. InChI      num removed protons in restored InChi */
            if (nNumRemovedProtons.nNumRemovedProtons != pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons)
            {
                /* restored structure InChI has less or more removed protons */
                pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOBH_PROTONS;
#if ( bRELEASE_VERSION == 0 )
                /* debug output only */
                {
                    int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons
                        - nNumRemovedProtons.nNumRemovedProtons;
                    fprintf(stdout, "COMPARE_INCHI: %ld: %s %cM: Proton balance (Diff: %d, RevrsRem=%d)\n",
                        num_inp, szCurHdr ? szCurHdr : "Struct", iInchiRec ? 'R' : 'D',
                        pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons, num_H_AddedByRevrs);
                }
#endif
            }
            for (i = 0; i < NUM_H_ISOTOPES; i++)
            {
                if (nNumRemovedProtons.nNumRemovedIsotopicH[i] != pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i])
                {
                    pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOB_ISO_H;
#if ( bRELEASE_VERSION == 0 )
                    /* debug output only */
                    {
                        int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i]
                            - nNumRemovedProtons.nNumRemovedIsotopicH[i];
                        fprintf(stdout, "COMPARE_INCHI: %ld: %s %cM: Iso Xchg %dH balance (Diff: %d, RevrsRem=%d)\n",
                            num_inp, szCurHdr ? szCurHdr : "Struct", iInchiRec ? 'R' : 'D', i + 1,
                            pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i], num_H_AddedByRevrs);
                    }
#endif
                }
            }
        }
    }

exit_function:

    return ret;
}


/****************************************************************************/
int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI* pStruct[INCHI_NUM][TAUT_NUM],
    InpInChI* pOneInput,
    int bHasSomeFixedH,
    long num_inp,
    char* szCurHdr)
{
    int i, k, m, n, iInChI, iMobileH, bMobileH, ifk;
    int num_components_D, num_components_R;
    int nNumCompHaveSeparateProtons_D, nNumCompHaveSeparateProtons_R;
    int num_fragments_D, num_fragments_R, num_fragments_DR, num_fragments, iComponent, ret;
    int ifInChI, ifMobileH, bfMobileH, nLink;
    COMPONENT_REM_PROTONS nNumRemovedProtons_D;     /* removed from the disconnected layer of the Input InChI */
    COMPONENT_REM_PROTONS nNumRemovedProtons_D_all; /* if only totals are avalable */
    COMPONENT_REM_PROTONS nNumRemovedProtons_R; /* removed from disconnected layer of the reconstructed struct */
    COMPONENT_REM_PROTONS nNumRemovedProtons_R_all;
    INCHI_MODE  CompareInchiFlags[TAUT_NUM];
    StrFromINChI* pStruct1;
    INChI_Aux* pINChI_Aux;
    INCHI_SORT* pINChISort1 = NULL; /* from reversed structure */
    INCHI_SORT* pINChISort2 = NULL; /* original input InChI */
    int        nNumNonTaut1 = 0, nNumNonTaut2 = 0;

    ret = 0;
    memset(pOneInput->CompareInchiFlags[1], 0, sizeof(pOneInput->CompareInchiFlags[1])); /* djb-rwth: memset_s C11/Annex K variant? */

    /* count components that are not subject to disconnection */
    if (!pOneInput->nNumComponents[INCHI_REC][TAUT_YES] &&
        !pOneInput->nNumComponents[INCHI_REC][TAUT_NON])
    {
        return 0; /* nothing to do */
    }

    memset(&nNumRemovedProtons_D, 0, sizeof(nNumRemovedProtons_D)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&nNumRemovedProtons_R, 0, sizeof(nNumRemovedProtons_R)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&nNumRemovedProtons_D_all, 0, sizeof(nNumRemovedProtons_D_all)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(&nNumRemovedProtons_R_all, 0, sizeof(nNumRemovedProtons_R_all)); /* djb-rwth: memset_s C11/Annex K variant? */
    memset(CompareInchiFlags, 0, sizeof(CompareInchiFlags)); /* djb-rwth: memset_s C11/Annex K variant? */

    num_components_D = inchi_max(pOneInput->nNumComponents[INCHI_BAS][TAUT_YES],
        pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]);
    num_components_R = inchi_max(pOneInput->nNumComponents[INCHI_REC][TAUT_YES],
        pOneInput->nNumComponents[INCHI_REC][TAUT_NON]);

    /***********************************************************************************************/
    /* InpInChI: count fragments -- disconnected components that do not match reconnected          */
    /* Accumulate removed H and isotopic H from ALL Fixed-H disconnected components except deleted */
    /* This segment collects info from the original InChI                                          */
    /***********************************************************************************************/
    /*---- Original InChI ----*/

    num_fragments_D = 0;
    iInChI = INCHI_BAS;
    iMobileH = bHasSomeFixedH ? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
    nNumCompHaveSeparateProtons_D = 0;

    /* in case of Mobile-H components here are the proton totals from the original InChI disconn. layer */
    nNumRemovedProtons_D.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons;
    memcpy(nNumRemovedProtons_D.nNumRemovedIsotopicH,
        pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH,
        sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH)); /* total for the disconnected layer */
    for (k = 0; k < num_components_D; k++)
    {
        bMobileH = iMobileH;
        if (!bInpInchiComponentExists(pOneInput, iInChI, bMobileH, k))
        {
            if (bInpInchiComponentExists(pOneInput, iInChI, TAUT_YES, k))
            {
                bMobileH = TAUT_YES;
            }
            else
            {
                continue; /* component is missing ??? */
            }
        }
        if (0 > (nLink = pOneInput->pInpInChI[iInChI][bMobileH][k].nLink))
        {
            /* component in Disconnected layer is linked to the identical one in the Reconnected layer */
            if (pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons)
            {
                nNumCompHaveSeparateProtons_D++;
                nLink = -(1 + nLink);
                nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedProtons;
                for (m = 0; m < NUM_H_ISOTOPES; m++)
                {
                    nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedIsotopicH[m];
                }
            }
            continue; /* same as reconnected */
        }
        /* component in the reconnected layer that was disconnected */
        nNumNonTaut2 += (bMobileH == TAUT_NON);
        if (pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons)
        {
            nNumCompHaveSeparateProtons_D++;
            nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons;
            for (m = 0; m < NUM_H_ISOTOPES; m++)
            {
                nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m];
            }
        }
        num_fragments_D++; /* number of disconnected fragments from original reconnected structure */
    }
    /* in case of Mobile-H components here are the proton totals from the original InChI */
    /*
    nNumRemovedProtons_D_all.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons;
    memcpy( nNumRemovedProtons_D_all.nNumRemovedIsotopicH,
            pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH,
            sizeof(nNumRemovedProtons_D_all.nNumRemovedIsotopicH) );

    */
    /****************************************************************************************************/
    /* count fragments in reconstructed reconnected structure                                           */
    /* accumulate removed H and isotopic H from ALL reconstructed reconnected components except deleted */
    /* This segment collects info from the reconstructed structure InChI                                */
    /****************************************************************************************************/
    /*---- InChI from the reconstructed reconnected structure ----*/
    num_fragments_R = 0;
    iInChI = INCHI_REC;
    iMobileH = bHasSomeFixedH ? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
    nNumCompHaveSeparateProtons_R = 0;
    for (k = 0; k < num_components_R; k++)
    {
        bMobileH = iMobileH;
        if (!bInpInchiComponentExists(pOneInput, iInChI, bMobileH, k))
        {
            if (bInpInchiComponentExists(pOneInput, iInChI, TAUT_YES, k))
            {
                bMobileH = TAUT_YES;
            }
            else
            {
                continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */
            }
        }
        if (0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink)
        {
            /* this reconstructed reconnected component was NOT DISCONNECTED */
            /* same component is in the disconnected layer, it has no metal atoms or is an isolated metal atom */
            pStruct1 = pStruct[iInChI][bMobileH] + k;
            ifMobileH = TAUT_YES;  /* Mobile-H Aux_Info contains number removed protons */
            ifInChI = INCHI_BAS; /* this component cannot be reconnected */
            ifk = 0;         /* 0th component since it is InChI of a single component */
            /* The statement in the following line is *WRONG*, component number mixed with bMobileH:  */
            /* in RevInchi, when only Mobile-H is present then its only non-NULL InChI has index 0==TAUT_NON */
            if (bRevInchiComponentExists(pStruct1, ifInChI, ifMobileH, ifk))
            {
                /* count protons */
                pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][ifMobileH];
                if (pINChI_Aux)
                {
                    nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
                    for (m = 0; m < NUM_H_ISOTOPES; m++)
                    {
                        nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m];
                    }
                }
            }
            nNumCompHaveSeparateProtons_R += bRevInchiComponentExists(pStruct1, ifInChI, ALT_TAUT(ifMobileH), ifk);
            continue; /* same as disconnected, has no metal atoms */
        }
        /* this reconstructed reconnected component WAS DISCONNECTED; check its fragments */
        /* it does not have same component in the disconnected layer */
        pStruct1 = pStruct[iInChI][bMobileH] + k;
        num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS];
        ifInChI = INCHI_BAS; /* disconnected layer */
        ifMobileH = bHasSomeFixedH ? TAUT_NON : TAUT_YES;
        for (ifk = 0; ifk < num_fragments; ifk++)
        {
            bfMobileH = ifMobileH;
            if (!bRevInchiComponentExists(pStruct1, ifInChI, bfMobileH, ifk))
            {
                if (bRevInchiComponentExists(pStruct1, ifInChI, TAUT_YES, ifk))
                {
                    bfMobileH = TAUT_YES;
                }
                else
                {
                    continue; /* fragment does not exist ??? */
                }
            }
            nNumNonTaut1 += (bfMobileH == TAUT_NON);
            nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON);
            /* count protons from fragments made by metal disconnection */
            pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES];
            if (pINChI_Aux)
            {
                nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
                for (m = 0; m < NUM_H_ISOTOPES; m++)
                {
                    nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m];
                }
            }
            num_fragments_R++; /* number of disconnected fragments from reconstructed reconnected structure */
        }
    }

    /*---------------- special treatment of the last reconstructed component -----------------*/
    /*---------------- this may contain separate protons added by the reconstruction ---------*/
    k = num_components_R - 1;
    pStruct1 = pStruct[iInChI][iMobileH] + k;
    if (iMobileH == TAUT_YES && !bHasSomeFixedH &&
        bInpInchiComponentDeleted(pOneInput, iInChI, iMobileH, k) &&
        (num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]))
    {

        ifInChI = INCHI_BAS; /* disconnected layer */
        ifMobileH = TAUT_YES;
        for (ifk = 0; ifk < num_fragments; ifk++)
        {
            bfMobileH = ifMobileH;
            if (!bRevInchiComponentDeleted(pStruct1, ifInChI, bfMobileH, ifk))
            {
                continue; /* fragment does exist ??? Should not happen */
            }
            /*
            nNumNonTaut1           += (bfMobileH == TAUT_NON);
            nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON);
            */
            /* count protons from fragments made by metal disconnection */
            pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES];
            if (pINChI_Aux)
            {
                nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
                for (m = 0; m < NUM_H_ISOTOPES; m++)
                {
                    nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m];
                }
            }
            /*num_fragments_R ++;*/ /* number of disconnected fragments from reconstructed reconnected structure */
        }
    }

    num_fragments_DR = inchi_max(num_fragments_D, num_fragments_R);
    /* in case of correct reconstruction, num_fragments_D, num_fragments_R */
    if (!num_fragments_DR)
    {
        return 0; /* no component was disconnected */
    }
    if (num_fragments_D != num_fragments_R)
    {
        for (i = 0; i < TAUT_NUM; i++)
        {
            if (pOneInput->nNumComponents[INCHI_BAS][i])
            {
                pOneInput->CompareInchiFlags[1][i] |= INCHIDIFF_PROBLEM;
            }
        }
        return 1; /* severe error */
    }

    pINChISort1 = (INCHI_SORT*)inchi_calloc(num_fragments_DR, sizeof(pINChISort1[0]));
    pINChISort2 = (INCHI_SORT*)inchi_calloc(num_fragments_DR, sizeof(pINChISort2[0]));
    if (!pINChISort1 || !pINChISort2)
    {
        ret = RI_ERR_ALLOC;
        goto exit_function;
    }

    /* accumulate original InChI of fragments -- disconnected components that do not match reconnected */
    iInChI = INCHI_BAS;
    iMobileH = bHasSomeFixedH ? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
    for (k = n = 0; k < num_components_D; k++)
    {
        bMobileH = iMobileH;
        if (!bInpInchiComponentExists(pOneInput, iInChI, bMobileH, k))
        {
            if (bInpInchiComponentExists(pOneInput, iInChI, TAUT_YES, k))
            {
                bMobileH = TAUT_YES;
            }
            else
            {
                continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */
            }
        }
        if (0 > pOneInput->pInpInChI[iInChI][bMobileH][k].nLink)
        {
            continue; /* same as reconnected */
        }
        /* the component exists in disconnected layer of the orig. InChI only: it is a fragment */
        pINChISort2[n].pINChI[bMobileH] = pOneInput->pInpInChI[iInChI][bMobileH] + k;
        if (bMobileH == TAUT_NON &&
            (bInpInchiComponentExists(pOneInput, iInChI, TAUT_YES, k) ||
                bInpInchiComponentDeleted(pOneInput, iInChI, TAUT_YES, k)))
        {
            pINChISort2[n].pINChI[TAUT_YES] = pOneInput->pInpInChI[iInChI][TAUT_YES] + k;
        }
        /* the last sort key is a number of removed protons */
        pINChISort2[n].ord_number = pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons ?
            pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons : 0;
        pINChISort2[n].n1 = k;  /* orig. InChI disconnected layer component number */
        pINChISort2[n].n2 = -1; /* no fragment index */
        n++;
    }

    /* accumulate fragments from the reconstructed structure */
    iInChI = INCHI_REC;
    iMobileH = bHasSomeFixedH ? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
    for (k = n = 0; k < num_components_R; k++)
    {
        bMobileH = iMobileH;
        if (!bInpInchiComponentExists(pOneInput, iInChI, bMobileH, k))
        {
            if (bInpInchiComponentExists(pOneInput, iInChI, TAUT_YES, k))
            {
                bMobileH = TAUT_YES;
            }
            else
            {
                continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */
            }
        }
        /* the reconstructed structure */
        if (0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink)
        {
            continue; /* same as disconnected, has no metal atoms */
        }
        /* this reconstructed structure was disconnected */
        pStruct1 = pStruct[iInChI][bMobileH] + k;
        num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS];
        ifInChI = INCHI_BAS;
        ifMobileH = bHasSomeFixedH ? TAUT_NON : TAUT_YES;
        for (i = 0; i < num_fragments; i++)
        {
            bfMobileH = ifMobileH;
            if (!bRevInchiComponentExists(pStruct1, ifInChI, bfMobileH, i))
            {
                if (bRevInchiComponentExists(pStruct1, ifInChI, TAUT_YES, i))
                {
                    bfMobileH = TAUT_YES;
                }
                else
                {
                    continue; /* component is missing ??? */
                }
            }
            pINChISort1[n].pINChI[bfMobileH] = pStruct1->RevInChI.pINChI[ifInChI][i][bfMobileH];
            if (bfMobileH == TAUT_NON /*&& bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i )*/)
            {
                pINChISort1[n].pINChI[TAUT_YES] = pStruct1->RevInChI.pINChI[ifInChI][i][TAUT_YES];
                /* remove Fixed-H InChI if is is identical to Mobile-H */
                /* do it exactly same way the identical components were removed from InpInChI */
                if (!CompareReversedINChI(pINChISort1[n].pINChI[bfMobileH],
                    pINChISort1[n].pINChI[TAUT_YES], NULL, NULL))
                {
                    pINChISort1[n].pINChI[bfMobileH] = NULL; /* remove Fixed-H layer */
                }
                else
                {
                    pINChISort1[n].ord_number = pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedProtons;
                }
            }

            pINChISort1[n].n1 = k;  /* reconstructed reconnected structure component index */
            pINChISort1[n].n2 = i;  /* index of a fragment made out of this component */
            n++;
        }
    }

    /* sort fragment InChI before comparing them */
    qsort(pINChISort1, num_fragments_D, sizeof(pINChISort1[0]), CompINChITaut2);
    qsort(pINChISort2, num_fragments_R, sizeof(pINChISort2[0]), CompINChITaut2);

    /* compare fragments -- components present in disconnected layer only */
    for (iComponent = 0; iComponent < num_fragments_DR; iComponent++)
    {
        INChI* pInChI1[TAUT_NUM]; /* from reversed structure */
        INChI* pInChI2[TAUT_NUM]; /* original input InChI */
        for (i = 0; i < TAUT_NUM; i++)
        {
            pInChI1[i] = pINChISort1[iComponent].pINChI[i];
            pInChI2[i] = pINChISort2[iComponent].pINChI[i];
        }
        CompareTwoPairsOfInChI(pInChI1, pInChI2, !bHasSomeFixedH, CompareInchiFlags);
    }

    if ( /*nNumNonTaut1 && nNumNonTaut2 &&*/ bHasSomeFixedH)
    {
        if (nNumCompHaveSeparateProtons_D || nNumCompHaveSeparateProtons_R)
        {
            /* for each component, compare number removed protons */
            /* comparison does not make sense if Disconnected Fixed-H layer is not present */
            for (iComponent = 0; iComponent < num_fragments_DR; iComponent++)
            {
                NUM_H   nNumRemovedIsotopicH1[NUM_H_ISOTOPES];
                NUM_H   nNumRemovedIsotopicH2[NUM_H_ISOTOPES];

                memset(nNumRemovedIsotopicH1, 0, sizeof(nNumRemovedIsotopicH1)); /* djb-rwth: memset_s C11/Annex K variant? */
                memset(nNumRemovedIsotopicH2, 0, sizeof(nNumRemovedIsotopicH2)); /* djb-rwth: memset_s C11/Annex K variant? */
                /* compare removed protons */
                if (pINChISort1[iComponent].ord_number != pINChISort2[iComponent].ord_number)
                {
                    CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */
                }
                /* also compare removed isotopic atoms H */
                k = pINChISort2[iComponent].n1; /* input InChI, OneInput */
                if (pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons)
                {
                    memcpy(nNumRemovedIsotopicH2,
                        pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH,
                        sizeof(nNumRemovedIsotopicH2));
                }
                /* get fragments of reconstructed structure removed protons info */
                k = pINChISort1[iComponent].n1; /* restored component number */
                i = pINChISort1[iComponent].n2; /* subcomponent number */
                iInChI = INCHI_REC;
                iMobileH = bHasSomeFixedH ? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES;
                bMobileH = iMobileH;
                if (!bInpInchiComponentExists(pOneInput, iInChI, bMobileH, k))
                {
                    if (bInpInchiComponentExists(pOneInput, iInChI, TAUT_YES, k))
                    {
                        bMobileH = TAUT_YES;
                    }
                    else
                    {
                        goto compare_iso_H;
                    }
                }
                if (pOneInput->pInpInChI[iInChI][bMobileH][k].nLink)
                {
                    continue;
                    /*
                    ret = RI_ERR_PROGR;
                    goto exit_function;
                    */
                }
                pStruct1 = pStruct[iInChI][bMobileH] + k;
                num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS];
                ifInChI = INCHI_BAS;
                ifMobileH = bHasSomeFixedH ? TAUT_NON : TAUT_YES;
                if (i < num_fragments)
                {
                    bfMobileH = ifMobileH;
                    if (!bRevInchiComponentExists(pStruct1, ifInChI, bfMobileH, i))
                    {
                        if (!bRevInchiComponentExists(pStruct1, ifInChI, TAUT_YES, i))
                            /* djb-rwth: removing redundant code */
                        {
                            goto compare_iso_H;
                        }
                    }
                    memcpy(nNumRemovedIsotopicH1,
                        pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedIsotopicH,
                        sizeof(nNumRemovedIsotopicH1));
                }
            compare_iso_H:
                if (memcmp(nNumRemovedIsotopicH1, nNumRemovedIsotopicH2, sizeof(nNumRemovedIsotopicH1)))
                {
                    CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H;
                }
            }
        }
    }
    else /*if ( !nNumNonTaut1 && !nNumNonTaut2 || !bHasSomeFixedH )*/
    {
        /* compare totals for removed protons and isotopic H */
        if (pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedProtons !=
            nNumRemovedProtons_R.nNumRemovedProtons)
        {
            CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS;
        }
        if (memcmp(pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedIsotopicH,
            nNumRemovedProtons_R.nNumRemovedIsotopicH,
            sizeof(nNumRemovedProtons_R.nNumRemovedIsotopicH)))
        {
            CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H;
        }
    }

    if (!nNumNonTaut1 == !nNumNonTaut2)
    {
        ; /* difference if(nNumNonTaut1 != nNumNonTaut2) will be caught in InChI comparison */
    }
    else
    {
        if (nNumNonTaut1)
        {
            /* reconstructed has Fixed-H while the original has not: extra Fixed-H layer */
            CompareInchiFlags[TAUT_YES] |= INCHIDIFF_WRONG_TAUT;
        }
        else
        {
            /* the original InChI has Fixed-H while the reconstructed one has not: missing Fixed-H layer */
            CompareInchiFlags[TAUT_YES] |= INCHIDIFF_NO_TAUT;
        }
    }
    for (i = 0; i < TAUT_NUM; i++)
    {
        pOneInput->CompareInchiFlags[1][i] |= CompareInchiFlags[i];
    }

    /* compare totals */
    if (nNumRemovedProtons_R.nNumRemovedProtons != nNumRemovedProtons_D.nNumRemovedProtons)
    {
        CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */
    }
    if (memcmp(nNumRemovedProtons_R.nNumRemovedIsotopicH,
        nNumRemovedProtons_D.nNumRemovedIsotopicH,
        sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH)))
    {
        CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H;
    }

exit_function:

    if (pINChISort1)
    {
        inchi_free(pINChISort1);
    }
    if (pINChISort2)
    {
        inchi_free(pINChISort2);
    }

    return ret;
}


/****************************************************************************/
int CompareTwoPairsOfInChI(INChI* pInChI1[TAUT_NUM],
    INChI* pInChI2[TAUT_NUM],
    int bMobileH,
    INCHI_MODE CompareInchiFlags[])
{
    int iMobileH, err = 0;
    INCHI_MODE cmp;
    for (iMobileH = 0; iMobileH < TAUT_NUM; iMobileH++)
    {
        if (!pInChI1[iMobileH] != !pInChI2[iMobileH])
        {
            if (iMobileH == TAUT_NON &&
                pInChI1[TAUT_YES] && pInChI2[TAUT_YES]) /* djb-rwth: condition corrected */
            {
                CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_HLAYER;
            }
            else
            {
                CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_NUMBER;
            }
            continue;
        }
        if (pInChI1[iMobileH] && pInChI2[iMobileH])
        {
            cmp = CompareReversedINChI3(pInChI1[iMobileH], pInChI2[iMobileH], NULL, NULL, &err);
            if (cmp)
            {
                CompareInchiFlags[iMobileH] |= cmp;
            }
        }
    }

    return err;
}


/****************************************************************************/
int CompareOneOrigInchiToRevInChI(StrFromINChI* pStruct,
    INChI* pInChI[TAUT_NUM],
    int bMobileH,
    int iComponent,
    long num_inp,
    char* szCurHdr,
    COMPONENT_REM_PROTONS* nCurRemovedProtons,
    INCHI_MODE CompareInchiFlags[])
{
    int ret = pStruct->RevInChI.nRetVal, err = 0;
    INCHI_MODE cmp;
    if ((ret == _IS_OKAY || ret == _IS_WARNING) && pStruct) /* djb-rwth: fixing a NULL pointer dereference */
    {
        /* ignore bMobileH for now */
        int i, i0, b /* created type */, b0 /* requested type*/, j, k;
        /* pINChI[iINCHI][iComponent][bTaut] */
        /* i0 = requested Rec/Disconnected: 1/0 */
        /* i  = what InChI creaded out of the restored structure */
        /* b0 = requested Mobile/Fixed-H: 1/0 */
        /* b  = what InChI creaded out of the restored structure */
        i = i0 = pStruct->iINCHI;
        b = b0 = pStruct->iMobileH;
        if (i == INCHI_REC && !pStruct->RevInChI.num_components[i])
        {
            i = INCHI_BAS;
        }
        if (b == TAUT_NON && (!pStruct->RevInChI.pINChI[i] ||
            !pStruct->RevInChI.pINChI[i][0][b] ||
            !pStruct->RevInChI.pINChI[i][0][b]->nNumberOfAtoms))
        {
            b = TAUT_YES;
        }
        if (pStruct->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted))
        {
            return 0;
        }

        if (pStruct->RevInChI.pINChI[i]) /* djb-rwth: fixing a NULL pointer dereference */
        {
            if ((pStruct->RevInChI.num_components[i] > 1 &&
                !pStruct->RevInChI.pINChI[i][1][b]->bDeleted) ||
                pStruct->RevInChI.num_components[i] < 1) /* djb-rwth: addressing LLVM warning */
            {
                CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_NUMBER;
            }
        }
        if (b != b0 || b != bMobileH || b0 != bMobileH || i > i0)
        {
            /* do not print messages about TAUT_YES instead of TAUT_NON */
            CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_HLAYER;
        }

        if (pStruct->RevInChI.num_components[i] && pStruct->RevInChI.pINChI[i]) /* djb-rwth: fixing a NULL pointer dereference */
        {
            /* compare InChI from restored structure; '0' in [i][0][b] is the first component */
            if (b == TAUT_YES && pStruct->RevInChI.pINChI[i][0][b]->bDeleted && ( !pInChI[0] || pInChI[0]->bDeleted ))
            {
                /* the 1st component is made out of proton(s) and the input component is missing or also a proton */
                cmp = 0;
            }
            else
            {
                cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][b], pInChI[0], NULL, NULL, &err );
                if (cmp)
                {
                    CompareInchiFlags[bMobileH] |= cmp;
                }
            }
            if (b == b0 && b == TAUT_NON)
            {
                if ((pStruct->RevInChI.pINChI[i][0][TAUT_YES] &&
                    !pStruct->RevInChI.pINChI[i][0][TAUT_YES]->bDeleted) ||
                    (pInChI[1] && !pInChI[1]->bDeleted)) /* djb-rwth: addressing LLVM warnings */
                {
                    /* in addition to fixed-H also compare mobile-H InChI */
                    cmp = CompareReversedINChI3(pStruct->RevInChI.pINChI[i][0][TAUT_YES], pInChI[1], NULL, NULL, &err);
                    if (cmp)
                    {
                        CompareInchiFlags[TAUT_YES] |= cmp;
                    }
                }
                /* compare removed H */
                if (pStruct->nNumRemovedProtonsMobHInChI != pStruct->RevInChI.pINChI_Aux[i][0][TAUT_YES]->nNumRemovedProtons)
                {
                    CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS;
                }
            }
            memset(nCurRemovedProtons, 0, sizeof(*nCurRemovedProtons)); /* djb-rwth: memset_s C11/Annex K variant? */
            for (k = 0; k < pStruct->RevInChI.num_components[i]; k++)
            {
                if (!k || pStruct->RevInChI.pINChI[i][k][TAUT_YES]->bDeleted)
                {
                    /* get removed protons from the 1st component; add othere only if they are deleted protons */
                    nCurRemovedProtons->nNumRemovedProtons += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedProtons;
                    for (j = 0; j < NUM_H_ISOTOPES; j++)
                    {
                        nCurRemovedProtons->nNumRemovedIsotopicH[j] += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedIsotopicH[j];
                    }
                }
            }
        }
    }
    else
    {
        CompareInchiFlags[bMobileH] |= INCHIDIFF_STR2INCHI_ERR;
    }

    return err;
}


/****************************************************************************/
INCHI_MODE CompareReversedStereoINChI3(INChI_Stereo* s1,
    /* InChI from reversed struct   */
    INChI_Stereo* s2,
    /* input InChI                  */
    ICR* picr)
{
    int ret = 0;
    int j1, j2, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; /* djb-rwth: removing redundant variables */
    int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only);
    int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only);

    int nNumSc1 = s1 ? s1->nNumberOfStereoCenters : 0;
    int nNumSc2 = s2 ? s2->nNumberOfStereoCenters : 0;
    int nNumSb1 = s1 ? s1->nNumberOfStereoBonds : 0;
    int nNumSb2 = s2 ? s2->nNumberOfStereoBonds : 0;

    if ((nNumSc1 || nNumSc2) &&
        (nNumSc1 != nNumSc2 ||
            memcmp(s1->nNumber, s2->nNumber, nNumSc1 * sizeof(s1->nNumber[0])) ||
            memcmp(s1->t_parity, s2->t_parity, nNumSc1 * sizeof(s1->t_parity[0])))) /* djb-rwth: condition corrected */
    {

        num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; /* djb-rwth: removing redundant code */
        for (j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; )
        {
            if (s1->nNumber[j1] == s2->nNumber[j2])
            {
                if (s1->t_parity[j1] != s2->t_parity[j2])
                    /* djb-rwth: removing redundant code */
                {
                    num_dif++;
                }
                j1++;
                j2++;
            }
            else
            {
                if (s1->nNumber[j1] < s2->nNumber[j2])
                {
                    num_in1_only++;
                    if (s1->t_parity[j1] == AB_PARITY_UNDF)
                    {
                        num_extra_undf++;
                    }
                    if (bAddSc)
                    {
                        if (picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY)
                            picr->sc_in1_only[picr->num_sc_in1_only++] = j1;
                        if (s1->t_parity[j1] == AB_PARITY_UNDF)
                        {
                            if (picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF)
                                picr->sc_undef_in1_only[picr->num_sc_undef_in1_only++] = j1;
                        }
                    }
                    j1++;
                }
                else
                {
                    num_in2_only++;
                    if (s2->t_parity[j2] == AB_PARITY_UNDF)
                    {
                        num_miss_undf++;
                    }
                    if (bAddSc)
                    {
                        if (picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY)
                            picr->sc_in2_only[picr->num_sc_in2_only++] = j2;
                        if (s2->t_parity[j2] == AB_PARITY_UNDF)
                        {
                            if (picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF)
                                picr->sc_undef_in2_only[picr->num_sc_undef_in2_only++] = j1;
                        }
                    }
                    j2++;
                }
            }
        }

        while (j1 < nNumSc1)
        {
            if (s1->t_parity[j1] == AB_PARITY_UNDF)
            {
                num_extra_undf++;
            }
            num_in1_only++;
            if (bAddSc)
            {
                if (picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY)
                    picr->sc_in1_only[picr->num_sc_in1_only++] = j1;
                if (s1->t_parity[j1] == AB_PARITY_UNDF)
                {
                    if (picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF)
                        picr->sc_undef_in1_only[picr->num_sc_undef_in1_only++] = j1;
                }
            }
            j1++;
        }

        while (j2 < nNumSc2)
        {
            if (s2->t_parity[j2] == AB_PARITY_UNDF)
            {
                num_miss_undf++;
            }
            num_in2_only++;
            if (bAddSc)
            {
                if (picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY)
                    picr->sc_in2_only[picr->num_sc_in2_only++] = j2;
            }
            j2++;
        }

        if (num_dif)
        {
            ret |= INCHIDIFF_SC_PARITY;
        }
        if (num_in1_only)
        {
            if (num_extra_undf)
            {
                ret |= INCHIDIFF_SC_EXTRA_UNDF;
            }
            if (num_in1_only != num_extra_undf)
            {
                ret |= INCHIDIFF_SC_EXTRA;
            }
        }
        if (num_in2_only)
        {
            if (num_miss_undf)
            {
                ret |= INCHIDIFF_SC_MISS_UNDF;
            }
            if (num_in2_only != num_miss_undf)
            {
                ret |= INCHIDIFF_SC_MISS;
            }
        }
    }

    if (s1 && s2 && (s2->nCompInv2Abs != 2) && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs)
    {
        ret |= INCHIDIFF_SC_INV; /* 2007-07-13 DT: added (s2->nCompInv2Abs != 2) to fix bug reoprted by Yerin on 2007/02/28 */
        /* Bug description: falsely reported "Stereo centers/allenes: Falsely inverted" for /S2 or /S3 */
    }

    if ((nNumSb1 || nNumSb2) &&
        (nNumSb1 != nNumSb2 ||
            memcmp(s1->nBondAtom1, s2->nBondAtom1, nNumSb1 * sizeof(s1->nBondAtom1[0])) ||
            memcmp(s1->nBondAtom2, s2->nBondAtom2, nNumSb1 * sizeof(s1->nBondAtom2[0])) ||
            memcmp(s1->b_parity, s2->b_parity, nNumSb1 * sizeof(s1->b_parity[0]))))
    {

        num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; /* djb-rwth: removing redundant code */
        for (j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; )
        {
            if (s1->nBondAtom1[j1] == s2->nBondAtom1[j2] &&
                s1->nBondAtom2[j1] == s2->nBondAtom2[j2])
            {
                if (s1->b_parity[j1] != s2->b_parity[j2])
                    /* djb-rwth: removing redundant code */
                {
                    num_dif++;
                }
                j1++;
                j2++;
            }
            else
            {
                if (s1->nBondAtom1[j1] < s2->nBondAtom1[j2] ||
                    (s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2])) /* djb-rwth: addressing LLVM warning */
                {
                    num_in1_only++;
                    if (s1->b_parity[j1] == AB_PARITY_UNDF)
                    {
                        num_extra_undf++;
                    }
                    if (bAddSb)
                    {
                        if (picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY)
                            picr->sb_in1_only[picr->num_sb_in1_only++] = j1;
                        if (s1->b_parity[j1] == AB_PARITY_UNDF)
                        {
                            if (picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF)
                                picr->sb_undef_in1_only[picr->num_sb_undef_in1_only++] = j1;
                        }
                    }
                    j1++;
                }
                else
                {
                    num_in2_only++;
                    if (s2->b_parity[j2] == AB_PARITY_UNDF)
                    {
                        num_miss_undf++;
                    }
                    if (bAddSb)
                    {
                        if (picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY)
                            picr->sb_in2_only[picr->num_sb_in2_only++] = j2;
                        if (s2->b_parity[j2] == AB_PARITY_UNDF)
                        {
                            if (picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF)
                                picr->sb_undef_in2_only[picr->num_sb_undef_in2_only++] = j1;
                        }
                    }
                    j2++;
                }
            }
        }
        while (j1 < nNumSb1)
        {
            num_in1_only++;
            if (s1->b_parity[j1] == AB_PARITY_UNDF)
            {
                num_extra_undf++;
            }
            if (bAddSb)
            {
                if (picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY)
                    picr->sb_in1_only[picr->num_sb_in1_only++] = j1;
                if (s1->b_parity[j1] == AB_PARITY_UNDF)
                {
                    if (picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF)
                        picr->sb_undef_in1_only[picr->num_sb_undef_in1_only++] = j1;
                }
            }
            j1++;
        }
        while (j2 < nNumSb2)
        {
            num_in2_only++;
            if (s2->b_parity[j2] == AB_PARITY_UNDF)
            {
                num_miss_undf++;
            }
            if (bAddSb)
            {
                if (picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY)
                    picr->sb_in2_only[picr->num_sb_in2_only++] = j2;
                if (s2->b_parity[j2] == AB_PARITY_UNDF)
                {
                    if (picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF)
                        picr->sb_undef_in2_only[picr->num_sb_undef_in2_only++] = j1;
                }
            }
            j2++;
        }
        if (num_dif)
        {
            ret |= INCHIDIFF_SB_PARITY;
        }
        if (num_in1_only)
        {
            if (num_extra_undf)
            {
                ret |= INCHIDIFF_SB_EXTRA_UNDF;
            }
            if (num_in1_only != num_extra_undf)
            {
                ret |= INCHIDIFF_SB_EXTRA;
            }
        }
        if (num_in2_only)
        {
            if (num_miss_undf)
            {
                ret |= INCHIDIFF_SB_MISS_UNDF;
            }
            if (num_in2_only != num_miss_undf)
            {
                ret |= INCHIDIFF_SB_MISS;
            }
        }
    }

    return ret;
}


/****************************************************************************/
INCHI_MODE CompareReversedINChI3(INChI* i1 /* InChI from reversed struct */,
    INChI* i2 /* input InChI */,
    INChI_Aux* a1,
    INChI_Aux* a2,
    int* err)
{
    INCHI_MODE ret = 0;
    INChI_Stereo* Stereo1 = NULL, * Stereo2 = NULL;
    int  n1, n2, m, j, j1, j2, ret2, num_H1, num_H2;
    ICR icr;
    ICR* picr = &icr;

    *err = 0;

    memset(picr, 0, sizeof(*picr)); /* djb-rwth: memset_s C11/Annex K variant? */

    if (i1 == NULL && i2 == NULL)
    {
        return 0;
    }
    if ((i1 == NULL) ^ (i2 == NULL))
    {
        ret |= INCHIDIFF_PROBLEM; /* one InChI exists while another doesn't */
        goto exit_function;
    }

    if (i1->nErrorCode == i2->nErrorCode)
    {
        if (i1->nErrorCode)
        {
            ret |= INCHIDIFF_PROBLEM; /* both InChI have same error codes */
            goto exit_function;
        }
    }
    else
    {
        ret |= INCHIDIFF_PROBLEM; /* at least one InChI has an error code */
        goto exit_function;
    }

    if (i1->nNumberOfAtoms != i2->nNumberOfAtoms)
    {
        ret |= INCHIDIFF_NUM_AT;
        goto exit_function;
    }
    if (i1->nNumberOfAtoms > 0)
    {
        if (memcmp(i1->nAtom, i2->nAtom, i1->nNumberOfAtoms * sizeof(i1->nAtom[0])))
        {
            ret |= INCHIDIFF_ATOMS;
            goto exit_function;
        }
        /* INCHIDIFF_NON_TAUT_H,  INCHIDIFF_MORE_FH, INCHIDIFF_LESS_FH */
        if (memcmp(i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms * sizeof(i1->nNum_H[0])))
        {
            ret |= INCHIDIFF_POSITION_H;
            for (j1 = 0; j1 < i1->nNumberOfAtoms; j1++)
            {
                if (i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H)
                {
                    picr->diff_pos_H_at[picr->num_diff_pos_H] = j1;
                    picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1];
                    picr->num_diff_pos_H++;
                }
            }
        }
        /* fixed H */
        if (i1->nNum_H_fixed || i2->nNum_H_fixed)
        {
            int bHasFixedH1 = 0, bHasFixedH2 = 0, i;
            if (i1->nNum_H_fixed)
            {
                for (i = 0; i < i1->nNumberOfAtoms; i++)
                {
                    if (i1->nNum_H_fixed[i])
                    {
                        bHasFixedH1++;
                    }
                }
            }
            if (i2->nNum_H_fixed)
            {
                for (i = 0; i < i2->nNumberOfAtoms; i++)
                {
                    if (i2->nNum_H_fixed[i])
                    {
                        bHasFixedH2++;
                    }
                }
            }
            if (bHasFixedH1 && !bHasFixedH2)
            {
                for (i = j = 0; i < i1->nNumberOfAtoms; i++)
                {
                    if (i1->nNum_H_fixed[i])
                    {
                        if (j < ICR_MAX_DIFF_FIXED_H)
                        {
                            picr->fixed_H_at1_more[j] = i;
                            picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i];
                            j++;
                        }
                    }
                }
                picr->num_fixed_H1_more = j;
                ret |= INCHIDIFF_MORE_FH; /* Extra Fixed-H */
            }
            else
            {
                if (!bHasFixedH1 && bHasFixedH2)
                {
                    for (i = j = 0; i < i2->nNumberOfAtoms; i++)
                    {
                        if (i2->nNum_H_fixed[i])
                        {
                            if (j < ICR_MAX_DIFF_FIXED_H)
                            {
                                picr->fixed_H_at2_more[j] = i;
                                picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i];
                                j++;
                            }
                        }
                    }
                    picr->num_fixed_H2_more = j;
                    ret |= INCHIDIFF_LESS_FH; /* Missed Fixed-H */
                }
                else
                {
                    if (bHasFixedH1 && bHasFixedH2 &&
                        memcmp(i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms * sizeof(i1->nNum_H_fixed[0])))
                    {
                        for (i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i++)
                        {
                            if (i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i])
                            {
                                if (j1 < ICR_MAX_DIFF_FIXED_H)
                                {
                                    picr->fixed_H_at1_more[j1] = i;
                                    picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i];
                                    j1++;
                                }
                            }
                            else
                                if (i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i])
                                {
                                    if (j2 < ICR_MAX_DIFF_FIXED_H)
                                    {
                                        picr->fixed_H_at2_more[j2] = i;
                                        picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i];
                                        j2++;
                                    }
                                }
                        }
                        ret |= (j1 ? INCHIDIFF_MORE_FH : 0) | (j2 ? INCHIDIFF_LESS_FH : 0);
                        picr->num_fixed_H1_more = j1;
                        picr->num_fixed_H2_more = j2;
                    }
                }
            }
        }
    }

    /* compare formulas and H */
    num_H1 = 0;
    num_H2 = 0;
    ret2 = CompareHillFormulasNoH(i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2);
    picr->tot_num_H1 = num_H1;
    picr->tot_num_H2 = num_H2;
    if (ret2)
    {
        ret |= INCHIDIFF_NUM_EL;
        goto exit_function;
    }
    if (num_H1 > num_H2)
    {
        ret |= INCHIDIFF_MORE_H;
    }
    if (num_H1 < num_H2)
    {
        ret |= INCHIDIFF_LESS_H;
    }

    if (i1->lenConnTable != i2->lenConnTable)
    {
        ret |= INCHIDIFF_CON_LEN;
        goto exit_function;
    }
    else
    {
        if (i1->lenConnTable > 0 && memcmp(i1->nConnTable, i2->nConnTable, i1->lenConnTable * sizeof(i1->nConnTable[0])))
        {
            ret |= INCHIDIFF_CON_TBL;
            goto exit_function;
        }
    }
    /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */
    /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */
        /*
    if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) )
    {
        ret |=  INCHIDIFF_TAUT_LEN;
    }
    */

    /* compare number of t-groups */
    n1 = i1->lenTautomer ? i1->nTautomer[0] : 0;
    n2 = i2->lenTautomer ? i2->nTautomer[0] : 0;
    if (!n1 && n2)
    {
        ret |= INCHIDIFF_NO_TAUT;
    }
    else
    {
        if (n1 && !n2)
        {
            ret |= INCHIDIFF_WRONG_TAUT;
        }
        else
        {
            if (n1 == 1 && n2 > 1)
            {
                ret |= INCHIDIFF_SINGLE_TG;
            }
            else
            {
                if (n1 > 1 && n2 == 1)
                {
                    ret |= INCHIDIFF_MULTIPLE_TG;
                }
                else
                {
                    if (n1 != n2)
                    {
                        ret |= INCHIDIFF_NUM_TG;
                    }
                }
            }
        }
    }

    if (n1 || n2)
    {
        /* number of endpoints */
        int num1 = 0, num2 = 0, num_M1 = 0, num_M2 = 0;
        int len, num_eq, num_in1_only, num_in2_only;
        AT_NUMB* pe1 = (AT_NUMB*)inchi_malloc(((long long)i1->lenTautomer + 1) * sizeof(pe1[0])); /* djb-rwth: cast operator added */
        AT_NUMB* pe2 = (AT_NUMB*)inchi_malloc(((long long)i2->lenTautomer + 1) * sizeof(pe2[0])); /* djb-rwth: cast operator added */
        num_H1 = num_H2 = 0;
        /* collect endpoints, H, (-) */
        if (!pe1 || !pe2)
        {
            if (pe1) inchi_free(pe1);
            if (pe2) inchi_free(pe2);
            *err = RI_ERR_ALLOC; /* allocation error */
            goto exit_function;
        }
        for (m = 1; m < i1->lenTautomer; m += len)
        {
            len = i1->nTautomer[m++];
            num_H1 += i1->nTautomer[m];
            num_M1 += i1->nTautomer[m + 1];
            for (j = 2; j < len; j++)
            {
                pe1[num1++] = i1->nTautomer[m + j];
            }
        }
        for (m = 1; m < i2->lenTautomer; m += len)
        {
            len = i2->nTautomer[m++];
            num_H2 += i2->nTautomer[m];
            num_M2 += i2->nTautomer[m + 1];
            for (j = 2; j < len; j++)
            {
                pe2[num2++] = i2->nTautomer[m + j];
            }
        }
        picr->num_taut_H1 = num_H1;
        picr->num_taut_H2 = num_H2;
        picr->num_taut_M1 = num_M1;
        picr->num_taut_M2 = num_M2;
        /* sort endpoints */
        insertions_sort_AT_NUMB(pe1, num1);
        insertions_sort_AT_NUMB(pe2, num2);
        /* compare */
        /*
        if ( num1 < num2 ) {
            ret |= INCHIDIFF_LESS_TG_ENDP;
        } else
        if ( num1 > num2 ) {
            ret |= INCHIDIFF_MORE_TG_ENDP;
        }
        */
        /* compare all */
        num_eq = num_in1_only = num_in2_only = 0;
        for (j1 = j2 = 0; j1 < num1 && j2 < num2; )
        {
            if (pe1[j1] == pe2[j2])
            {
                j1++;
                j2++;
                num_eq++;
            }
            else
            {
                if (pe1[j1] < pe2[j1])
                {
                    if (picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY)
                    {
                        picr->endp_in1_only[picr->num_endp_in1_only++] = pe1[j1];
                    }
                    j1++;
                    num_in1_only++;
                }
                else
                {
                    if (picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY)
                    {
                        picr->endp_in2_only[picr->num_endp_in2_only++] = pe2[j2];
                    }
                    j2++;
                    num_in2_only++;
                }
            }
        }
        while (j1 < num1)
        {
            if (picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY)
            {
                picr->endp_in1_only[picr->num_endp_in1_only++] = pe1[j1];
            }
            j1++;
            num_in1_only++;
        }
        while (j2 < num2)
        {
            if (picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY)
            {
                picr->endp_in2_only[picr->num_endp_in2_only++] = pe2[j2];
            }
            j2++;
            num_in2_only++;
        }
        if (num_in1_only)
        {
            ret |= INCHIDIFF_EXTRA_TG_ENDP;
        }
        if (num_in2_only)
        {
            ret |= INCHIDIFF_MISS_TG_ENDP;
        }
        if (!num_in1_only && !num_in2_only && num_eq)
        {
            ; /* same t-groups endpoints */
        }
        else
        {
            ret |= INCHIDIFF_DIFF_TG_ENDP;
        }
        inchi_free(pe1);
        inchi_free(pe2);
    }

    if ((i1->lenTautomer > 1 && i2->lenTautomer > 1) &&
        (i1->lenTautomer != i2->lenTautomer ||
            memcmp(i1->nTautomer, i2->nTautomer, i1->lenTautomer * sizeof(i1->nTautomer[0]))))
        ret |= INCHIDIFF_TG;

    if (i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms)
    {
        ret |= INCHIDIFF_NUM_ISO_AT;
    }
    else
    {
        if (i1->nNumberOfIsotopicAtoms > 0 && memcmp(i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms * sizeof(i1->IsotopicAtom[0])))
        {
            ret |= INCHIDIFF_ISO_AT;
        }
    }
    if (i1->nTotalCharge != i2->nTotalCharge)
        ret |= INCHIDIFF_CHARGE;
    if (a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons))
    {
        ret |= INCHIDIFF_REM_PROT;
    }
    if (a1 && (!a2 ||
        a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] ||
        a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] ||
        a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]))
    {
        ret |= INCHIDIFF_REM_ISO_H;
    }

    /*
    if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) {
        if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] ||
             memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH,
                    sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) )
            return 18;
    } else
    if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) {
        return 19;
    }
    */
    if (i1->StereoIsotopic &&
        i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters)
    {
        Stereo1 = i1->StereoIsotopic;
    }
    else
    {
        Stereo1 = i1->Stereo;
    }
    if (i2->StereoIsotopic &&
        i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters)
    {
        Stereo2 = i2->StereoIsotopic;
    }
    else
    {
        Stereo2 = i2->Stereo;
    }
    ret |= CompareReversedStereoINChI3(Stereo1, Stereo2, picr);

exit_function:
    picr->flags = ret;

    return ret;
}


/* message group names */
const CMP_INCHI_MSG_GROUP CompareInchiMsgsGroup[] =
{
    {IDGRP_ERR,     " Error:"},
    {IDGRP_H,       " Hydrogens:"},
    {IDGRP_MOB_GRP, " Mobile-H groups:"},
    {IDGRP_ISO_AT,  " Isotopic:"},
    {IDGRP_CHARGE,  " Charge(s):"},
    {IDGRP_PROTONS, " Proton balance:"},
    {IDGRP_ISO_H,   " Exchangeable isotopic H:"},
    {IDGRP_SC,      " Stereo centers/allenes:"},
    {IDGRP_SB,      " Stereobonds/cumulenes:"},
    {IDGRP_HLAYER,  " Fixed-H layer:"},
    {IDGRP_COMP,    " Number of components:"},
    {IDGRP_CONV_ERR," Conversion encountered:"},
    {IDGRP_ZERO,    ""}
};



/* messages */
const CMP_INCHI_MSG  CompareInchiMsgs[] =
{
    {INCHIDIFF_PROBLEM      ,IDGRP_ERR,     " Wrong result"                   }, /*0x00000001,  severe: at least one InChI does not exist */
    {INCHIDIFF_POSITION_H   ,IDGRP_H,       " Locations or number"            }, /*0x00000002,  difference in non-taut {Mobile-H} or all H {Fixed-H} location/number */
    {INCHIDIFF_MORE_FH      ,IDGRP_H,       " Fixed-H"                        }, /*0x00000004,  extra fixed H */
    {INCHIDIFF_LESS_FH      ,IDGRP_H,       " Fixed-H"                        }, /*0x00000004,  missing fixed H */
    {INCHIDIFF_MORE_H       ,IDGRP_H,       " Number"                         }, /*0x00000008,  formulas differ in number of H */
    {INCHIDIFF_LESS_H       ,IDGRP_H,       " Number"                         }, /*0x00000008,  formulas differ in number of H */
    {INCHIDIFF_NO_TAUT      ,IDGRP_MOB_GRP, " Missing"                        }, /*0x00000010,  restored structure has no taut groups while the original InChI has some */
    {INCHIDIFF_WRONG_TAUT   ,IDGRP_MOB_GRP, " Falsely present"                }, /*0x00000020,  restored has tautomerism while the original does not have it */
    {INCHIDIFF_SINGLE_TG    ,IDGRP_MOB_GRP, " One instead of multiple"        }, /*0x00000040,  restored has 1 taut. group while the original InChI has multiple tg */
    {INCHIDIFF_MULTIPLE_TG  ,IDGRP_MOB_GRP, " Multiple instead of one"        }, /*0x00000080,  restored has multiple tg while the original InChI has only one tg */
    {INCHIDIFF_EXTRA_TG_ENDP,IDGRP_MOB_GRP, " Attachment points"              }, /*0x00000100,  extra tautomeric endpoint{s} in restored structure */
    {INCHIDIFF_MISS_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points"              }, /*0x00000100,  one or more tg endpoint is not in the restored structure */
    {INCHIDIFF_DIFF_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points"              }, /*0x00000100,  lists of tg endpoints are different */
    {INCHIDIFF_NUM_TG       ,IDGRP_MOB_GRP, " Number"                         }, /*0x00000200,  different number of tautomeric groups */
    {INCHIDIFF_TG           ,IDGRP_MOB_GRP, " Do not match"                   }, /*0x00000200,  different tautomeric groups */
    {INCHIDIFF_NUM_ISO_AT   ,IDGRP_ISO_AT,  " Atoms do not match"             }, /*0x00000400,  ?severe: restored struct. has different number of isotopic atoms */
    {INCHIDIFF_ISO_AT       ,IDGRP_ISO_AT,  " Atoms do not match"             }, /*0x00000400,  ?severe: restored struct. has different locations/isotopes of isotopic atoms */
    {INCHIDIFF_REM_ISO_H    ,IDGRP_ISO_H,   " Does not match for a component" }, /*0x00000800,  isotopic H removed */
    {INCHIDIFF_MOB_ISO_H    ,IDGRP_ISO_H,   " Do not match"                   }, /*0x00001000,  different number of mobile exchangeable isotopic H */
    {INCHIDIFF_CHARGE       ,IDGRP_CHARGE,  " Do not match"                   }, /*0x00002000,  restored structure has different charge */
    {INCHIDIFF_REM_PROT     ,IDGRP_PROTONS, " Does not match for a component" }, /*0x00004000,  proton{s} removed/added from the restored structure */
    {INCHIDIFF_MOBH_PROTONS ,IDGRP_PROTONS, " Does not match"                 }, /*0x00008000,  different proton balance */
    {INCHIDIFF_SC_INV       ,IDGRP_SC,      " Falsely inverted"               }, /*0x00010000,  restores structure has different inversion stereocenter mark */
    {INCHIDIFF_SC_PARITY    ,IDGRP_SC,      " Wrong parity"                   }, /*0x00020000,  restored structure has stereoatoms or allenes with different parity */
    {INCHIDIFF_SC_EXTRA_UNDF,IDGRP_SC,      " Extra undefined"                }, /*0x00040000,  restored structure has extra undefined stereocenter{s} */
    {INCHIDIFF_SC_EXTRA     ,IDGRP_SC,      " Extra known"                    }, /*0x00080000,  restored structure has extra stereocenter{s} */
    {INCHIDIFF_SC_MISS_UNDF ,IDGRP_SC,      " Missing undefined"              }, /*0x00100000,  restored structure has not some undefined stereocenter{s} */
    {INCHIDIFF_SC_MISS      ,IDGRP_SC,      " Missing known"                  }, /*0x00200000,  restored structure has not some stereocenters that are not undefined */
    {INCHIDIFF_SB_PARITY    ,IDGRP_SB,      " Wrong parity"                   }, /*0x00400000,  restored structure has stereobonds or cumulenes with different parity */
    {INCHIDIFF_SB_EXTRA_UNDF,IDGRP_SB,      " Extra undefined"                }, /*0x00800000,  restored structure has extra undefined stereobond{s} */
    {INCHIDIFF_SB_EXTRA     ,IDGRP_SB,      " Missing known"                  }, /*0x01000000,  restored structure has extra stereobond{s} */
    {INCHIDIFF_SB_MISS_UNDF ,IDGRP_SB,      " Missing undefined"              }, /*0x02000000,  restored structure has not some undefined stereocenters */
    {INCHIDIFF_SB_MISS      ,IDGRP_SB,      " Missing known"                  }, /*0x04000000,  restored structure has not some stereobonds that are not undefined */
    {INCHIDIFF_COMP_HLAYER  ,IDGRP_HLAYER,  " Missing or extra"               }, /*0x08000000,  Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */
    {INCHIDIFF_COMP_NUMBER  ,IDGRP_COMP,    " Does not match"                 }, /*0x10000000,  wrong number of components */
    {INCHIDIFF_STR2INCHI_ERR,IDGRP_CONV_ERR," Error"                          },  /*0x20000000   Restored structure to InChI conversion error */
    {INCHIDIFF_ZERO         ,IDGRP_ZERO,    ""                                }
};


/*************************************************************************/
int AddOneMsg(char* szMsg,
    int used_len,
    int tot_len,
    const char* szAddMsg,
    const char* szDelim)
{
    const char ellip[] = "...";
    int len = (int)strlen(szAddMsg);
    int len_delim = (used_len && szDelim) ? strlen(szDelim) : 0;
    int len_to_copy;
    if (len + len_delim + used_len < tot_len)
    {
        if (len_delim)
        {
            strcpy(szMsg + used_len, szDelim);
            used_len += len_delim;
        }
        strcpy(szMsg + used_len, szAddMsg);
        used_len += len;
    }
    else
    {
        if ((len_to_copy = (tot_len - used_len - len_delim - (int)sizeof(ellip))) > 10)
        {
            if (len_delim)
            {
                strcpy(szMsg + used_len, szDelim);
                used_len += len_delim;
            }
            strncpy(szMsg + used_len, szAddMsg, len_to_copy);
            used_len += len_to_copy;
            strcpy(szMsg + used_len, ellip);
            used_len += sizeof(ellip) - 1;
        }
    }

    return used_len;
}


/****************************************************************************/
int FillOutCompareMessage(char* szMsg, int nLenMsg, INCHI_MODE bits[])
{
    int bMobileH, k, n, len = (int)strlen(szMsg);
    int iPrevGrpIdx, iCurGrpIdx, bFound;
    INCHI_MODE bit;
    static const char* hdr = " Problems/mismatches:";
    char szOneMsg[256];

    int mismatch = 0;

    if (bits[TAUT_YES] || bits[TAUT_NON])
    {

        mismatch = -1;

        if (!strstr(szMsg, hdr))
        {
            len = AddOneMsg(szMsg, len, nLenMsg, hdr, NULL);
        }

        for (bMobileH = TAUT_YES; 0 <= bMobileH; bMobileH--)
        {
            /*      bMobileH = TAUT_YES, TAUT_NON */

            if (bits[bMobileH])
            {
                strcpy(szOneMsg, bMobileH == TAUT_YES ? " Mobile-H(" : " Fixed-H(");
                len = AddOneMsg(szMsg, len, nLenMsg, szOneMsg, NULL);
            }

            bit = 1;
            iPrevGrpIdx = -1;

            do
            {
                if (bit & bits[bMobileH])
                {
                    /* search for the message */
                    bFound = 0;
                    for (k = 0; CompareInchiMsgs[k].nBit != INCHIDIFF_ZERO && !bFound; k++)
                    {
                        if (bit & (INCHI_MODE)CompareInchiMsgs[k].nBit)
                        {
                            /* message found */
                            for (n = 0; CompareInchiMsgsGroup[n].nGroupID != IDGRP_ZERO; n++)
                            {
                                if (CompareInchiMsgsGroup[n].nGroupID == CompareInchiMsgs[k].nGroupID)
                                {
                                    iCurGrpIdx = n;
                                    if (iCurGrpIdx != iPrevGrpIdx)
                                    {
                                        if (iPrevGrpIdx >= 0)
                                        {
                                            len = AddOneMsg(szMsg, len, nLenMsg, ";", NULL);
                                        }
                                        len = AddOneMsg(szMsg, len, nLenMsg, CompareInchiMsgsGroup[iCurGrpIdx].szGroupName, NULL);
                                    }
                                    len = AddOneMsg(szMsg, len, nLenMsg, CompareInchiMsgs[k].szMsg, iCurGrpIdx == iPrevGrpIdx ? "," : NULL);
                                    iPrevGrpIdx = iCurGrpIdx;
                                    bFound = 1;
                                    break;
                                }
                            }
                        }
                    }
                }
                bit <<= 1;
            } while (bit);

            if (bits[bMobileH])
            {
                len = AddOneMsg(szMsg, len, nLenMsg, ")", NULL);
            }

        }
    }

    return mismatch; /*len; */
}


#endif
