/*
 * 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 <ctype.h>

#include "mode.h"
#include "ichimake.h"
#include "inchi_api.h"
#include "ichimain.h"
#include "ichister.h"
#include "ichi_io.h"
#include "ichitime.h"
#include "ichi_bns.h"

#include "bcf_s.h"

/*
    Local functions
*/

int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at );
int GetElementAndCount( const char **f, char *szEl, int *count );
int CompareHillFormulas( const char *f1, const char *f2 );
int CompareInchiStereo( INChI_Stereo *Stereo1,
                        INCHI_MODE nFlags1,
                        INChI_Stereo *Stereo2,
                        INCHI_MODE nFlags2 );
int CompareReversedStereoINChI( INChI_Stereo *s1,
                                INChI_Stereo *s2 );
int GetAtomOrdNbrInCanonOrd( struct tagCANON_GLOBALS *pCG,
                             inp_ATOM *norm_at,
                             AT_NUMB *nAtomOrdNbr,
                             AT_NUMB *nOrigAtNosInCanonOrd,
                             int num_at );
int FillOutCanonInfAtom( struct tagCANON_GLOBALS *pCG,
                         inp_ATOM *norm_at,
                         INF_ATOM_DATA *inf_norm_at_data,
                         int init_num_at,
                         int bIsotopic,
                         INChI *pINChI,
                         INChI_Aux *pINChI_Aux,
                         int bAbcNumbers,
                         INCHI_MODE nMode );
int FillOutOneCanonInfAtom( struct tagCANON_GLOBALS *pCG,
                            inp_ATOM *inp_norm_at,
                            INF_ATOM_DATA *inf_norm_at_data,
                            AT_NUMB *pStereoFlags,
                            int init_num_at,
                            int offset,
                            int offset_H,
                            int bIsotopic,
                            INChI *pINChI,
                            INChI_Aux *pINChI_Aux,
                            int bAbcNumbers,
                            INCHI_MODE nMode );
int FillOutInputInfAtom( inp_ATOM *inp_at,
                         INF_ATOM_DATA *inf_at_data,
                         int init_num_at,
                         int num_removed_H,
                         int bAdd_DT_to_num_H,
                         int nNumRemovedProtons,
                         NUM_H *nNumRemovedProtonsIsotopic,
                         int bIsotopic,
                         int bAbcNumbers );
int CheckCanonNumberingCorrectness( int num_atoms,
                                    int num_at_tg,
                                    sp_ATOM *at,
                                    CANON_STAT *pCS,
                                    CANON_GLOBALS *pCG,
                                    int bTautomeric,
                                    char *pStrErrStruct );
static int CompareDfsDescendants4CT( const void *a1, const void *a2, void* );
int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo );
#if ( READ_INCHI_STRING == 1 ) /*  { */
int CompareReversedStereoINChI2( INChI_Stereo *s1, INChI_Stereo *s2, ICR *picr );
#endif

/****************************************************************************/
int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at )
{
    int i, j, val;

    memset( at, 0, sizeof( at[0] )*num_inp_at ); /* djb-rwth: memset_s C11/Annex K variant? */

    for (i = 0; i < num_inp_at; i++)
    {
        strncpy(at[i].elname, inp_at[i].elname, sizeof(at[0].elname));
        at[i].el_number = (U_CHAR) get_periodic_table_number( at[i].elname );
        val = at[i].valence = inp_at[i].valence;
        for (j = 0; j < val; j++)
        {
            at[i].neighbor[j] = inp_at[i].neighbor[j];
            at[i].bond_type[j] = inp_at[i].bond_type[j];
        }
        at[i].chem_bonds_valence = inp_at[i].chem_bonds_valence;
        at[i].orig_at_number = inp_at[i].orig_at_number;
        at[i].orig_compt_at_numb = inp_at[i].orig_compt_at_numb;
        at[i].endpoint = inp_at[i].endpoint;
        at[i].iso_atw_diff = inp_at[i].iso_atw_diff;
        at[i].num_H = inp_at[i].num_H;
        at[i].cFlags = inp_at[i].cFlags;
        for (j = 0; j < NUM_H_ISOTOPES; j++)
        {
            at[i].num_iso_H[j] = inp_at[i].num_iso_H[j];
        }
        at[i].charge = inp_at[i].charge;
        at[i].radical = inp_at[i].radical;

#if ( FIND_RING_SYSTEMS == 1 )
        at[i].nBlockSystem = inp_at[i].nBlockSystem;
        at[i].bCutVertex = inp_at[i].bCutVertex;
        at[i].nRingSystem = inp_at[i].nRingSystem;
        at[i].nNumAtInRingSystem = inp_at[i].nNumAtInRingSystem;
#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 )
        at[i].nDistanceFromTerminal = inp_at[i].nDistanceFromTerminal;
#endif
#endif

/*
        at[i].x                  = inp_at[i].x;
        at[i].y                  = inp_at[i].y;
        at[i].z                  = inp_at[i].z;
*/
    }

    return 0;
}


/****************************************************************************/
int GetElementAndCount( const char **f, char *szEl, int *count )
{
    const char *p = *f;
    char       *q;
    int   i = 0;

    /* djb-rwth: fixing oss-fuzz issue #37224 */
    if (p && *p)
    {
        if (isupper( UCINT *p ))
        {
            szEl[i++] = *p++;
            if (*p && islower( UCINT *p ))
            {
                szEl[i++] = *p++;
            }
            szEl[i] = '\0';
            if (1 == i && szEl[0] == 'C')
            {
                szEl[0] = 'A'; /*  less than any element: */
                               /*  carbon-containing compounds should be first */
            }
            if (*p && isdigit( UCINT *p ))
            {
                *count = strtol( p, &q, 10 );
                p = q;
            }
            else
            {
                *count = 1;
            }
            *f = p; /*  next element; */
            return 1;
        }
        return -1; /*  not a chemical formula */
    }
    /* v. 1.06 Changed "Zz" to "Zzz" as "Zz" is valid symbol now */
    strcpy(szEl, "Zzz");
    /*strcpy( szEl, "Zz" );*/
    /*  zero termination 'element' is larger than any other element */
    *count = 99999;         /* zero termination 'element count' is larger than any other count */
    return 0;
}


/*
    CompareHillFormulas( ... )

       E1 < E2 if strcmp( E1, E2) < 0  OR E2 is empty and E1 is not
       n1 < n2 if value n1 > n2

    Sorting order:

        C10H22N
        C10H22
        C2
        Ag2Cl2
        Ag2Cl
        Ag2F2
        Ag2
        AgCl
        AgF
        F6S
        F2S
*/


/****************************************************************************/
int CompareHillFormulas( const char *f1, const char *f2 )
{
    char szEl1[4], szEl2[4];
    int  count1, count2, ret1, ret2, ret;

    do
    {
        ret1 = GetElementAndCount( &f1, szEl1, &count1 );
        ret2 = GetElementAndCount( &f2, szEl2, &count2 );
        if (0 <= ret1 && 0 <= ret2)
        {
            if ((ret = strcmp( szEl1, szEl2 ))) /* djb-rwth: addressing LLVM warning */
            {
                return ret; /*  lexicographic order, string termination > any character */
            }
            if ((ret = count2 - count1)) /* djb-rwth: addressing LLVM warning */
            {
                return ret; /*  inverse atom count order */
            }
        }
        else
        {
            return 0; /*  program error <BRKPT> */
        }
    }
    while (0 < ret1 && 0 < ret2);

    return 0;
}


/****************************************************************************/
int CompareHillFormulasNoH( const char *f1,
                            const char *f2,
                            int *num_H1,
                            int *num_H2 )
{
    char szEl1[4], szEl2[4];
    int  count1, count2, ret1, ret2, ret;

    do
    {
        ret1 = GetElementAndCount( &f1, szEl1, &count1 );
        if (0 < ret1 && szEl1[0] == 'H' && !szEl1[1])
        {
            *num_H1 += count1;
            ret1 = GetElementAndCount( &f1, szEl1, &count1 );
        }
        ret2 = GetElementAndCount( &f2, szEl2, &count2 );
        if (0 < ret2 && szEl2[0] == 'H' && !szEl2[1])
        {
            *num_H2 += count2;
            ret2 = GetElementAndCount( &f2, szEl2, &count2 );
        }
        if (0 <= ret1 && 0 <= ret2)
        {
            if ((ret = strcmp( szEl1, szEl2 ))) /* djb-rwth: addressing LLVM warning */
            {
                return ret; /*  lexicographic order, string termination > any character */
            }
            if ((ret = count2 - count1)) /* djb-rwth: addressing LLVM warning */
            {
                return ret; /*  inverse atom count order */
            }
        }
        else
        {
            return 0; /*  program error <BRKPT> */
        }
    }
    while (0 < ret1 && 0 < ret2);

    return 0;
}


/****************************************************************************/
int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 )
{
    int len1, len2, ret, i;

    len1 = i1->lenTautomer > 0 && i1->nTautomer[0] ? i1->lenTautomer : 0;
    len2 = i2->lenTautomer > 0 && i2->nTautomer[0] ? i2->lenTautomer : 0;
    if ((ret = len2 - len1)) /* djb-rwth: addressing LLVM warning */
    {
        return ret;
    }
    for (i = 0; i < len1; i++)
    {
        if ((ret = (int) i2->nTautomer[i] - (int) i1->nTautomer[i])) /* djb-rwth: addressing LLVM warning */
            return ret;
    }
    return 0;
}


/****************************************************************************
    CompINChITautVsNonTaut( ... )

    Sorting in descending order:
        return -1 if *p1 > *p2, return +1 if *p1 < *p2
****************************************************************************/
int CompINChITautVsNonTaut( const INCHI_SORT *p1,
                            const INCHI_SORT *p2,
                            int bCompareIsotopic )
{
    int ret, num, i, num_H1, num_H2;

    const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */
    const INChI *i2 = NULL; /* Fixed-H  layers in Fixed-H  sorting order */

    int   n1;               /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */

    /* INChI_Stereo *Stereo1, *Stereo2; */

    n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms ) ? TAUT_YES : TAUT_NON;

    i1 = p1->pINChI[n1];
    i2 = ( n1 == TAUT_YES && p2->pINChI[TAUT_NON] &&
           p2->pINChI[TAUT_NON]->nNumberOfAtoms ) ? p2->pINChI[TAUT_NON] : (const INChI *) NULL;

    /* non-deleted-non-empty < deleted < empty */
    if (i1 && !i2)
        return 0;   /* non-empty is the smallest (first) */
    if (!i1 && i2)
        return 0;
    if (!i1 && !i2)
        return 0;
    if (i1->bDeleted)
        return 1;    /* deleted is the largest (last) among non-empty */
    if (i2->bDeleted)
        return -1;

    if (i1->nNumberOfAtoms > 0 && !i2->nNumberOfAtoms)
        return 0;

    /* i2 = i2;         djb-rwth: an obviously useless statement */

    num_H1 = num_H2 = 0;

    /* do not compare terminal H */
    if ((ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ))) /* djb-rwth: addressing LLVM warning */
    {
        return ret;  /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */
    }

    /*
        compare non-isotopic non-tautomeric part
    */

    /* compare number of atoms (excluding terminal H) */
    if ((ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms)) /* djb-rwth: addressing LLVM warning */
        return ret; /*  more atoms first */

    /*  compare elements  (excluding terminal H) */
    num = i1->nNumberOfAtoms;
    for (i = 0; i < num; i++)
    { /* should always be equal if Hill formulas are same */
        if ((ret = (int) i2->nAtom[i] - (int) i1->nAtom[i])) /* djb-rwth: addressing LLVM warning */
            return ret; /* greater periodic number first */
    }
    /*
        compare connection tables
    */
    if ((ret = i2->lenConnTable - i1->lenConnTable)) /* djb-rwth: addressing LLVM warning */
        return ret; /* longer connection table first */
    num = i2->lenConnTable;
    for (i = 0; i < num; i++)
    {
        if ((ret = (int) i2->nConnTable[i] - (int) i1->nConnTable[i])) /* djb-rwth: addressing LLVM warning */
            return ret; /* greater connection table first */
    }
    /*
      compare total number of H (inverse: H3 < H2 )
    */
    if ((ret = num_H2 - num_H1)) /* djb-rwth: addressing LLVM warning */
        return ret;
    /*
      compare non-tautomeric num_H: N < NH3 < NH2 < NH
    */
    num = i1->nNumberOfAtoms;
    for (i = 0; i < num; i++)
    {
        if (i2->nNum_H[i] != i1->nNum_H[i])
        {
            return !i2->nNum_H[i] ? 1 :  /* no H first */
                !i1->nNum_H[i] ? -1 :
                (int) i2->nNum_H[i] - (int) i1->nNum_H[i];
        }
    }
    /*
        compare non-isotopic tautomeric part
    */
    if ((ret = CompareTautNonIsoPartOfINChI( i1, i2 ))) /* djb-rwth: addressing LLVM warning */
    {
        return ret;
    }
    /*
    if ( ret = i2->lenTautomer - i1->lenTautomer )
        return ret;
    num = inchi_min( i2->lenTautomer, i1->lenTautomer );
    for ( i = 0; i < num; i ++ ) {
        if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] )
            return ret;
    }
    */

    /*
        at this point both components are either tautomeric
        or non-tautomeric
     */

    /*
        non-tautomeric "fixed H" specific
    */
    if ( /*TAUT_NON == bTaut &&*/ ( i2 && i2->nNum_H_fixed ))
    {
        /* first, compare non-tautomeric chem. formulas -- they may be different */
        /* secondly, compare fixed-H distribution */
        if (i2->nNum_H_fixed)
        {
            num = i2->nNumberOfAtoms;
            for (i = 0; i < num; i++)
            {
                if (i2->nNum_H_fixed[i] != 0)
                {
                    return 1;
                }
            }
        }
    }
    /*
        compare non-isotopic stereo
    */
    ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags );
    if (ret)
    {
        return ret;
    }

    /*
        do not switch back to tautomeric i1, i2
    */

    /* -- how to switch back --
    if ( i1t ) {
        i1  = i1t;
        i1t = NULL;
    }
    if ( i2t ) {
        i2  = i2t;
        i2t = NULL;
    }
    */

    /*
         compare isotopic non-tautomeric part
    */
    if (bCompareIsotopic)
    {
        if ((ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms)) /* djb-rwth: addressing LLVM warning */
            return ret;
        num = i1->nNumberOfIsotopicAtoms;

        /*  compare isotopic atoms */
        for (i = 0; i < num; i++)
        {
            if ((ret = (int) i2->IsotopicAtom[i].nAtomNumber - (int) i1->IsotopicAtom[i].nAtomNumber)) /* djb-rwth: addressing LLVM warning */
                return ret;
            if ((ret = (int) i2->IsotopicAtom[i].nIsoDifference - (int) i1->IsotopicAtom[i].nIsoDifference)) /* djb-rwth: addressing LLVM warning */
                return ret;
        }

        /* compare isotopic H */
        /* if tautomeric comparison mode then here are compared only non-tautomeric H */
        for (i = 0; i < num; i++)
        {
            if ((ret = (int) i2->IsotopicAtom[i].nNum_T - (int) i1->IsotopicAtom[i].nNum_T)) /* djb-rwth: addressing LLVM warning */
                return ret;
            if ((ret = (int) i2->IsotopicAtom[i].nNum_D - (int) i1->IsotopicAtom[i].nNum_D)) /* djb-rwth: addressing LLVM warning */
                return ret;
            if ((ret = (int) i2->IsotopicAtom[i].nNum_H - (int) i1->IsotopicAtom[i].nNum_H)) /* djb-rwth: addressing LLVM warning */
                return ret;
        }

        /* compare isotopic tautomeric part */
        if ((ret = i2->nNumberOfIsotopicTGroups || i1->nNumberOfIsotopicTGroups)) /* djb-rwth: addressing LLVM warning */
            return ret;

        /*
        num = i1->nNumberOfIsotopicTGroups;
        for ( i = 0; i < num; i ++ ) {
            if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber )
                return ret;
            if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T )
                return ret;
            if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D )
                return ret;
            if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H )
                return ret;
        }
        */

        /* compare isotopic stereo */
        ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags,
                                  i2->StereoIsotopic, i2->nFlags );
        if (ret)
        {
            return ret;
        }
    }

    /*
        compare charges: non-charged first, then in order of
        ascending charges (negative first)
    */

    if (i2->nTotalCharge && i1->nTotalCharge)
    {
        /*  both are charged; smaller charges first */
        ret = (int) i1->nTotalCharge - (int) i2->nTotalCharge;
        return ret;
    }
    if ((ret = ( i1->nTotalCharge ? 1 : 0 ) - ( i2->nTotalCharge ? 1 : 0 ))) /* djb-rwth: addressing LLVM warning */
    {
        /*  only one is charged; uncharged first */
        return ret;
    }
    /* stable sort */
    /*ret = p1->ord_number - p2->ord_number;*/

    return ret;
}

/*
    Stereo
*/


/****************************************************************************/
typedef enum tagSp3StereoTypeTmp
{
    SP3_NONE = 0,  /* no sp3 stereo: no /t, /m, /s segments */
    /* /t is present: */
    SP3_ONLY = 1,  /* no /s or /m segment: inversion leaves the structure unchanged */
    SP3_ABS = 2,  /* abs stereo: both /m and /s are present */
    SP3_REL = 4,  /* rel stereo: /s is present, /m is not */
    SP3_RAC = 8,  /* racemic stereo: /s is presen, /m is nott */
    SP3_TYPE = ( SP3_ABS | SP3_REL | SP3_RAC ),          /* bitmap for checking the presence of /m */
    SP3_ANY = ( SP3_ABS | SP3_REL | SP3_RAC | SP3_ONLY )  /* bitmap for checking the presence of /t */
} SP3_TYPE_TMP;


/****************************************************************************/
int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo )
{
    int nRet = SP3_NONE;
    if (pINChI && !pINChI->bDeleted && Stereo && 0 < Stereo->nNumberOfStereoCenters)
    {
        if (0 != Stereo->nCompInv2Abs)
        {
            if (pINChI->nFlags & INCHI_FLAG_REL_STEREO)
            {
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                if (1 < Stereo->nNumberOfStereoCenters)
                {
                    nRet = SP3_REL;
                }
#else
                nRet = SP3_REL;
#endif
            }
            else
                if (pINChI->nFlags & INCHI_FLAG_RAC_STEREO)
                {
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                    if (1 < Stereo->nNumberOfStereoCenters)
                    {
                        nRet = SP3_REL;
                    }
#else
                    nRet = SP3_RAC;
#endif
                }
                else
                {
                    nRet = SP3_ABS;
                }
        }
        else
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
            if (!( ( pINChI->nFlags & ( INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO ) ) && 1 == Stereo->nNumberOfStereoCenters ))
#endif
            {
                nRet = SP3_ONLY; /*  SP3_NONE if relative stereo and 1 stereocenter */
            }
    }
    return nRet;
}


/****************************************************************************
CompINChILayers( ... )

 Sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2
****************************************************************************/
int CompINChILayers( const INCHI_SORT *p1,
                     const INCHI_SORT *p2,
                     char sDifSegs[][DIFS_LENGTH],
                     int bFixTranspChargeBug )
{
    int ret = 0, num, i, num_H1, num_H2;

    const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */
    const INChI *i2 = NULL; /* Fixed-H  layers in Fixed-H  sorting order */

    int   n1;               /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */

    INChI_Stereo *Stereo1, *Stereo2;
    INChI_Stereo *IsoStereo1, *IsoStereo2;
    int bRelRac[DIFL_LENGTH];
    char *psDifSegs;

    n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms ) ? TAUT_YES : TAUT_NON;

    i1 = p1->pINChI[n1];
    i2 = ( n1 == TAUT_YES && p2->pINChI[TAUT_NON] &&
           p2->pINChI[TAUT_NON]->nNumberOfAtoms ) ? p2->pINChI[TAUT_NON] : (const INChI *) NULL;

    num_H1 = num_H2 = 0;
    memset( bRelRac, DIFV_BOTH_EMPTY, sizeof( bRelRac ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    /*=====================*/
    /*====     /f    ======*/
    /*=====================*/
    if (i1 && !i1->bDeleted && i1->szHillFormula && i1->szHillFormula[0])
    {
        sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED;
        if (i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0])
        {
            if (!CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) &&
                  num_H1 == num_H2)
            {
                sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_EQL2PRECED;
            }
            else
            {
                sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED;
            }
        }
        else
        {
            sDifSegs[DIFL_F][DIFS_f_FORMULA] |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
        }
    }
    else
    {
        sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY;
        if (i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0])
        {
            sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED;
        }
        else
        {
            sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY;
        }
    }
    /*=====================*/
    /*====     /c    ======*/
    /*=====================*/
    if (i1 && !i1->bDeleted && i1->lenConnTable > 1)
    {
        sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED;
    }
    else
    {
        sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY;
    }
    /*=====================*/
    /*====     /h    ======*/
    /*=====================*/
    /* M: H atoms */
    if (i1 && !i1->bDeleted)
    {
        num_H1 = ( i1->lenTautomer > 0 && i1->nTautomer && i1->nTautomer[0] ) ? 1 : 0; /* number of t-groups */
        if (!num_H1 && i1->nNum_H)
        {
            for (i = 0; i < i1->nNumberOfAtoms; i++)
            { /* immobile H */
                if (i1->nNum_H[i])
                {
                    num_H1 = 1;
                    break;
                }
            }
        }
        sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= num_H1 ? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;
    }
    else
    {
        sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY;
    }
    /* F: fixed mobile H */
    if (i2 && !i2->bDeleted && i2->nNum_H_fixed)
    {
        num_H2 = 0;
        if (i1 && !i1->bDeleted)
        {
            for (i = 0; i < i1->nNumberOfAtoms; i++)
            {
                if (i2->nNum_H_fixed[i])
                {
                    num_H2 = 1;
                    break;
                }
            }
        }
        sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= num_H2 ? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;
    }
    else
    {
        sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY;
    }
    /* MI: exchangable isotopic H: see OutputINChI1(), num_iso_H[] */

    /*=====================*/
    /*====     /q    ======*/
    /*=====================*/
    psDifSegs = &sDifSegs[DIFL_F][DIFS_q_CHARGE];
    if (i1 && !i1->bDeleted)
    {
        if (i1->nTotalCharge)
        {
            sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED;
        }
        else
        {
            sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY;
        }
        if (i2 && !i2->bDeleted)
        {
            if (i1->nTotalCharge)
            {
                if (i1->nTotalCharge == i2->nTotalCharge)
                {
                    *psDifSegs |= DIFV_EQL2PRECED;
                }
                else
                {
                    if (i2->nTotalCharge)
                    {
                        *psDifSegs |= DIFV_NEQ2PRECED;
                    }
                    else
                    {
                        *psDifSegs |= DIFV_IS_EMPTY;
                    }
                }
            }
            else
            {
                if (i2->nTotalCharge)
                {
                    *psDifSegs |= DIFV_NEQ2PRECED;
                }
                else
                {
                    *psDifSegs |= DIFV_BOTH_EMPTY;
                }
            }
        }
        else
        {
            if (!i2)
            {
                if (bFixTranspChargeBug == 1)
                {
                    /* bug explanation:

                    component #1 is tautomeric, component #2 is not
                    Mobile-H(#2) > Mobile-H(#1)
                    Fixed-H(#2) = Mobile-H(#2) < Fixed-H(#1)

                    Layer       first_charge   second_charge

                    Mobile-H    0    (comp#1)  -1 (comp#2)
                    Fixed-H     none (comp#2)  -1 (comp#1)

                    v1.01 charge compared decided that charge layers are same and omitted Fixed-H /q layer

                    Solution: when component permutation is detected AND fixed-H component does not exist,
                    compare Mobile-H charge [0 (comp#1) in the example] to the charge of Mobile-H [-1 (comp#2)]
                    of the component that has none Fixed-H charge
                    */

                    /* Fixed-H i2 is empty because Fixed-H struct is same as Mobile-H */
                    if (p1->ord_number != p2->ord_number && /* component order in Fixed-H is different from Mobile-H */
                         n1 == TAUT_YES && p2->pINChI[TAUT_YES] && !p2->pINChI[TAUT_YES]->bDeleted &&
                        p2->pINChI[TAUT_YES]->nNumberOfAtoms)
                    {
                        int i2_nTotalCharge = p2->pINChI[TAUT_YES]->nTotalCharge;

                        if (i1->nTotalCharge)
                        {
                            if (i1->nTotalCharge == i2_nTotalCharge)
                            {
                                *psDifSegs |= DIFV_EQL2PRECED;
                            }
                            else
                            {
                                if (i2_nTotalCharge)
                                {
                                    *psDifSegs |= DIFV_NEQ2PRECED;
                                }
                                else
                                {
                                    *psDifSegs |= DIFV_IS_EMPTY;
                                }
                            }
                        }
                        else
                        {
                            if (i2_nTotalCharge)
                            {
                                *psDifSegs |= DIFV_NEQ2PRECED;
                            }
                            else
                            {
                                *psDifSegs |= DIFV_BOTH_EMPTY;
                            }
                        }
                    }
                    else
                    {
                        *psDifSegs |= i1->nTotalCharge ? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY;
                    }
                }
                else /* if (bFixTranspChargeBug==1) */
                {
                    *psDifSegs |= i1->nTotalCharge ? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY;
                }
            }
            else /* if ( !i2 ) { */
            {
                /* i2 && i2->bDeleted */
                *psDifSegs |= i1->nTotalCharge ? DIFV_IS_EMPTY : DIFV_BOTH_EMPTY;
            }
        }
    }
    else
    {
        sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY;
        if (i2 && !i2->bDeleted)
        {
            if (i2->nTotalCharge)
            {
                sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED;
            }
            else
            {
                sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY;
            }
        }
    }


    /*************** stereo *****************/

    if (i1 && !i1->bDeleted)
    {
        Stereo1 = i1->Stereo;
        IsoStereo1 = i1->StereoIsotopic;
    }
    else
    {
        Stereo1 = NULL;
        IsoStereo1 = NULL;
    }
    if (i2 && !i2->bDeleted)
    {
        Stereo2 = i2->Stereo;
        IsoStereo2 = i2->StereoIsotopic;
    }
    else
    {
        Stereo2 = NULL;
        IsoStereo2 = NULL;
    }

    /*=====================*/
    /*====     /b    ======*/
    /*=====================*/
    /* M double bond stereo */
    psDifSegs = &sDifSegs[DIFL_M][DIFS_b_SBONDS];
    if (Stereo1 && Stereo1->nNumberOfStereoBonds)
    {
        *psDifSegs |= DIFV_NEQ2PRECED;
    }
    else
    {
        *psDifSegs |= DIFV_BOTH_EMPTY;
    }
    /* F double bond stereo */
    psDifSegs = &sDifSegs[DIFL_F][DIFS_b_SBONDS];
    if (Stereo2 && Stereo2->nNumberOfStereoBonds)
    {
        if (Stereo1 && Stereo1->nNumberOfStereoBonds)
        {
            if (Eql_INChI_Stereo( Stereo1, EQL_SP2, Stereo2, EQL_SP2, 0 ))
            {
                *psDifSegs |= DIFV_EQL2PRECED;
            }
            else
            {
                *psDifSegs |= DIFV_NEQ2PRECED;
            }
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (Stereo1 && Stereo1->nNumberOfStereoBonds)
        {
            *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* MI double bond stereo */
    psDifSegs = &sDifSegs[DIFL_MI][DIFS_b_SBONDS];
    if (IsoStereo1 && IsoStereo1->nNumberOfStereoBonds)
    {
        if (Eql_INChI_Stereo( IsoStereo1, EQL_SP2, Stereo1, EQL_SP2, 0 ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (Stereo1 && Stereo1->nNumberOfStereoBonds)
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* FI double bond stereo */
    psDifSegs = &sDifSegs[DIFL_FI][DIFS_b_SBONDS];
    if (IsoStereo2 && IsoStereo2->nNumberOfStereoBonds)
    {
        if (Eql_INChI_Stereo( IsoStereo2, EQL_SP2, Stereo2, EQL_SP2, 0 ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            if (!( Stereo1 && Stereo1->nNumberOfStereoBonds ) &&
                 !( Stereo2 && Stereo2->nNumberOfStereoBonds ) &&
                 Eql_INChI_Stereo( IsoStereo2, EQL_SP2, IsoStereo1, EQL_SP2, 0 ))
            {
                *psDifSegs |= DIFV_FI_EQ_MI;
            }
            else
            {
                *psDifSegs |= DIFV_NEQ2PRECED;
            }
        }
    }
    else
    {
     /* the solution table for FI stereo,
        in case of FI stereo is empty
        E = segment is empty, NE = not empty
        +==============================+
        | M   | MI  | F   |  result    |
        +=====+=====+=====+============+
        | E   | E   | E   | both empty |
        +-----+-----+-----+------------+
        | NE  | E   | E   | both empty |
        +-----+-----+-----+------------+
        | E   | NE  | E   | is empty   |
        +-----+-----+-----+------------+
        | NE  | NE  | E   | both empty |
        +-----+-----+-----+------------+
        | E   | E   | NE  | is empty   |
        +-----+-----+-----+------------+
        | NE  | E   | NE  | is empty   |
        +-----+-----+-----+------------+
        | E   | NE  | NE  | is empty   |
        +-----+-----+-----+------------+
        | NE  | NE  | ME  | is empty   |
        +==============================+
     */
        if (Stereo2    && Stereo2->nNumberOfStereoBonds)
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
            if (IsoStereo1 && IsoStereo1->nNumberOfStereoBonds &&
                 !( Stereo1 && Stereo1->nNumberOfStereoBonds )
                )
            {
                *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
            }
            else
            {
                *psDifSegs |= DIFV_BOTH_EMPTY;
            }
    }

    /*==================================*/
    /*====     /t, /m, /s for M   ======*/
    /*==================================*/
    /* M sp3 stereo */
    bRelRac[DIFL_M] = GetSp3RelRacAbs( i1, Stereo1 );       /* Mobile-H */
    bRelRac[DIFL_MI] = GetSp3RelRacAbs( i1, IsoStereo1 );
    bRelRac[DIFL_F] = GetSp3RelRacAbs( i2, Stereo2 );       /* Fixed-H */
    bRelRac[DIFL_FI] = GetSp3RelRacAbs( i2, IsoStereo2 );
    if (SP3_NONE != bRelRac[DIFL_M])
    {
        sDifSegs[DIFL_M][DIFS_t_SATOMS] |= ( bRelRac[DIFL_M] & SP3_ANY ) ? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;
        sDifSegs[DIFL_M][DIFS_m_SP3INV] |= ( bRelRac[DIFL_M] & SP3_ABS ) ? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;
        sDifSegs[DIFL_M][DIFS_s_STYPE] |= ( bRelRac[DIFL_M] & SP3_TYPE ) ? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;
    }
    else
    {
        sDifSegs[DIFL_M][DIFS_t_SATOMS] |= DIFV_BOTH_EMPTY;
        sDifSegs[DIFL_M][DIFS_m_SP3INV] |= DIFV_BOTH_EMPTY;
        sDifSegs[DIFL_M][DIFS_s_STYPE] |= DIFV_BOTH_EMPTY;
    }

    /*=====================*/
    /*====     /t    ======*/
    /*=====================*/
    /* F sp3 stereo */
    psDifSegs = &sDifSegs[DIFL_F][DIFS_t_SATOMS];
    if (SP3_ANY & bRelRac[DIFL_F])
    {
        if (Eql_INChI_Stereo( Stereo2, EQL_SP3, Stereo1, EQL_SP3, 0 ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (SP3_ANY & bRelRac[DIFL_M])
        {
            *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* MI sp3 stereo */
    psDifSegs = &sDifSegs[DIFL_MI][DIFS_t_SATOMS];
    if (SP3_ANY & bRelRac[DIFL_MI])
    {
        if (Eql_INChI_Stereo( IsoStereo1, EQL_SP3, Stereo1, EQL_SP3, 0 ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (SP3_ANY & bRelRac[DIFL_M])
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* FI sp3 stereo */
    psDifSegs = &sDifSegs[DIFL_FI][DIFS_t_SATOMS];
    if (SP3_ANY & bRelRac[DIFL_FI])
    {
        if (Eql_INChI_Stereo( IsoStereo2, EQL_SP3, Stereo2, EQL_SP3, 0 ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            if (!( SP3_ANY & bRelRac[DIFL_M] ) &&
            !( SP3_ANY & bRelRac[DIFL_F] ) &&
            Eql_INChI_Stereo( IsoStereo2, EQL_SP3, IsoStereo1, EQL_SP3, 0 ))
            {
                *psDifSegs |= DIFV_FI_EQ_MI;
            }
            else
            {
                *psDifSegs |= DIFV_NEQ2PRECED;
            }
        }
    }
    else /* similar to /b */
        if (( SP3_ANY & bRelRac[DIFL_F] ))
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            if (( SP3_ANY & bRelRac[DIFL_MI] ) && !( SP3_ANY & bRelRac[DIFL_M] ))
            {
                *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
            }
            else
            {
                *psDifSegs |= DIFV_BOTH_EMPTY;
            }
        }

        /*=====================*/
        /*====     /m    ======*/
        /*=====================*/
        /* F sp3 abs stereo inversion */
    psDifSegs = &sDifSegs[DIFL_F][DIFS_m_SP3INV];
    if ((bRelRac[DIFL_F] & SP3_ABS) && Stereo1 && Stereo2) /* djb-rwth: fixing a NULL pointer dereference */
    {
        /* the order of || operands below is critically important: || is not a commutative operation */
        if (!( bRelRac[DIFL_M] & SP3_ABS ) || Stereo2->nCompInv2Abs != Stereo1->nCompInv2Abs)
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
    }
    else
    {
        if (bRelRac[DIFL_M] & SP3_ABS)
        {
            *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* MI sp3 abs stereo inversion */
    psDifSegs = &sDifSegs[DIFL_MI][DIFS_m_SP3INV];
    if ((SP3_ABS & bRelRac[DIFL_MI]) && Stereo1) /* djb-rwth: fixing a NULL pointer dereference */
    {
        if (( SP3_ABS & bRelRac[DIFL_M] ) && IsoStereo1->nCompInv2Abs == Stereo1->nCompInv2Abs)
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (SP3_ABS & bRelRac[DIFL_M])
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* FI sp3 abs stereo inversion */
    psDifSegs = &sDifSegs[DIFL_FI][DIFS_m_SP3INV];
    if ((SP3_ABS & bRelRac[DIFL_FI]) && IsoStereo1 && IsoStereo2 && Stereo2) /* djb-rwth: fixing a NULL pointer dereference */
    {
        if (( SP3_ABS & bRelRac[DIFL_F] ) && IsoStereo2->nCompInv2Abs == Stereo2->nCompInv2Abs)
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            if (!( SP3_ABS & bRelRac[DIFL_M] ) &&
            !( SP3_ABS & bRelRac[DIFL_F] ) &&
       ( SP3_ABS & bRelRac[DIFL_MI] ) && /* make sure IsoStereo1 != NULL */
            IsoStereo2->nCompInv2Abs == IsoStereo1->nCompInv2Abs)
            {
                *psDifSegs |= DIFV_FI_EQ_MI;
            }
            else
            {
                *psDifSegs |= DIFV_NEQ2PRECED;
            }
        }
    }

    else
    {
        /* similar to /b */
        /* the order of || operands below is critically important: || is no a commutative operation */
        if (( SP3_ABS & bRelRac[DIFL_F] ))
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            if (( SP3_ABS & bRelRac[DIFL_MI] ) && !( SP3_ABS & bRelRac[DIFL_M] ))
            {
                *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
            }
            else
            {
                *psDifSegs |= DIFV_BOTH_EMPTY;
            }
        }
    }

    /*=====================*/
    /*====     /s    ======*/
    /*=====================*/
    /* F sp3 stereo type */
    psDifSegs = &sDifSegs[DIFL_F][DIFS_s_STYPE];
    if (bRelRac[DIFL_F] & SP3_TYPE)
    {
        if (( bRelRac[DIFL_F] & SP3_TYPE ) == ( bRelRac[DIFL_M] & SP3_TYPE ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (bRelRac[DIFL_M] & SP3_TYPE)
        {
            *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* MI sp3 stereo type */
    psDifSegs = &sDifSegs[DIFL_MI][DIFS_s_STYPE];
    if (SP3_TYPE & bRelRac[DIFL_MI])
    {
        if (( SP3_TYPE & bRelRac[DIFL_MI] ) == ( SP3_TYPE & bRelRac[DIFL_M] ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            *psDifSegs |= DIFV_NEQ2PRECED;
        }
    }
    else
    {
        if (SP3_TYPE & bRelRac[DIFL_M])
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            *psDifSegs |= DIFV_BOTH_EMPTY;
        }
    }

    /* FI sp3 stereo type */
    psDifSegs = &sDifSegs[DIFL_FI][DIFS_s_STYPE];
    if (SP3_TYPE & bRelRac[DIFL_FI])
    {
        if (( SP3_TYPE & bRelRac[DIFL_FI] ) == ( SP3_TYPE & bRelRac[DIFL_F] ))
        {
            *psDifSegs |= DIFV_EQL2PRECED;
        }
        else
        {
            if (!( SP3_TYPE & bRelRac[DIFL_M] ) &&
            !( SP3_TYPE & bRelRac[DIFL_F] ) &&
       ( SP3_TYPE & bRelRac[DIFL_MI] ))
            {
                *psDifSegs |= DIFV_FI_EQ_MI;
            }
            else
            {
                *psDifSegs |= DIFV_NEQ2PRECED;
            }
        }
    }
    else
    {
        /* similar to /b */
        /* the order of || operands below is critically important: || is not a commutative operation */
        if (( SP3_TYPE & bRelRac[DIFL_F] ))
        {
            *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */
        }
        else
        {
            if (( SP3_TYPE & bRelRac[DIFL_MI] ) && !( SP3_TYPE & bRelRac[DIFL_M] ))
            {
                *psDifSegs |= i2 ? DIFV_IS_EMPTY : DIFV_EQL2PRECED;
            }
            else
            {
                *psDifSegs |= DIFV_BOTH_EMPTY;
            }
        }
    }

    /*=====================*/
    /*====     /o    ======*/
    /*=====================*/
    if (p1 && p2 && p1->ord_number != p2->ord_number)
    {
        sDifSegs[DIFL_F][DIFS_o_TRANSP] |= DIFV_NEQ2PRECED;
    }
    /*=====================*/
    /*====     /i    ======*/
    /*=====================*/

    /* M isotopic atoms */
    psDifSegs = &sDifSegs[DIFL_MI][DIFS_i_IATOMS];
    if (i1 && !i1->bDeleted && ( i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups ))
    {
        *psDifSegs |= DIFV_NEQ2PRECED;
    }
    else
    {
        *psDifSegs |= DIFV_BOTH_EMPTY;
    }
    /* F isotopic atoms */
    psDifSegs = &sDifSegs[DIFL_FI][DIFS_i_IATOMS];
    if (i2 && !i2->bDeleted)
    {
        if (i2->nNumberOfIsotopicAtoms || i2->nNumberOfIsotopicTGroups)
        {
            if (!i1 || i1->bDeleted ||
                 i2->nNumberOfIsotopicAtoms != i1->nNumberOfIsotopicAtoms ||
                 i2->nNumberOfIsotopicTGroups != i1->nNumberOfIsotopicTGroups)
            {
                *psDifSegs |= DIFV_NEQ2PRECED;
            }
            else
            {
                int diff;
                num = i1->nNumberOfIsotopicAtoms;
                diff = 0;
                for (i = 0; i < num; i++)
                {
                    /* compare isotopic atoms */
                    if ((diff = (int) i2->IsotopicAtom[i].nAtomNumber - (int) i1->IsotopicAtom[i].nAtomNumber)) /* djb-rwth: addressing LLVM warning */
                    {
                        break;
                    }
                    if ((diff = (int) i2->IsotopicAtom[i].nIsoDifference - (int) i1->IsotopicAtom[i].nIsoDifference)) /* djb-rwth: addressing LLVM warning */
                    {
                        break;
                    }
                    /* compare isotopic H */
                    if ((diff = (int) i2->IsotopicAtom[i].nNum_T - (int) i1->IsotopicAtom[i].nNum_T)) /* djb-rwth: addressing LLVM warning */
                    {
                        break;
                    }
                    if ((diff = (int) i2->IsotopicAtom[i].nNum_D - (int) i1->IsotopicAtom[i].nNum_D)) /* djb-rwth: addressing LLVM warning */
                    {
                        break;
                    }
                    if ((diff = (int) i2->IsotopicAtom[i].nNum_H - (int) i1->IsotopicAtom[i].nNum_H)) /* djb-rwth: addressing LLVM warning */
                    {
                        break;
                    }
                }
                if (!diff)
                {
                    num = i1->nNumberOfIsotopicTGroups;
                    for (i = 0; i < num; i++)
                    {
                        if ((diff = (int) i2->IsotopicTGroup[i].nTGroupNumber - (int) i1->IsotopicTGroup[i].nTGroupNumber)) /* djb-rwth: addressing LLVM warning */
                        {
                            break;
                        }
                        if ((diff = (int) i2->IsotopicTGroup[i].nNum_T - (int) i1->IsotopicTGroup[i].nNum_T)) /* djb-rwth: addressing LLVM warning */
                        {
                            break;
                        }
                        if ((diff = (int) i2->IsotopicTGroup[i].nNum_D - (int) i1->IsotopicTGroup[i].nNum_D)) /* djb-rwth: addressing LLVM warning */
                        {
                            return diff;
                        }
                        if ((diff = (int) i2->IsotopicTGroup[i].nNum_H - (int) i1->IsotopicTGroup[i].nNum_H)) /* djb-rwth: addressing LLVM warning */
                        {
                            break;
                        }
                    }
                }
                *psDifSegs |= diff ? DIFV_NEQ2PRECED : DIFV_FI_EQ_MI;
            }
        }
        else
        {
            if (i1 && !i1->bDeleted && ( i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups ))
            {
                *psDifSegs |= DIFV_IS_EMPTY;
            }
        }
    }
    else
    {
        if (!i2)
        {
            if (i1 && !i1->bDeleted && ( i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups ))
            {
                *psDifSegs |= DIFV_EQL2PRECED;
            }
            else
            {
                *psDifSegs |= DIFV_BOTH_EMPTY;
            }
        }
    }

    return ret;
}


/****************************************************************************/
int INChI_SegmentAction( char cDifSegs )
{
    if (!( cDifSegs & DIFV_OUTPUT_OMIT_F ))
    {
        return INCHI_SEGM_OMIT;
    }
    if (( cDifSegs & DIFV_OUTPUT_EMPTY_T ) && !( cDifSegs & DIFV_OUTPUT_EMPTY_F ))
    {
        return INCHI_SEGM_EMPTY;
    }
    if (( cDifSegs & DIFV_OUTPUT_FILL_T ))
    {
        return INCHI_SEGM_FILL;
    }

    return INCHI_SEGM_OMIT; /* the control flow shoul never reach this point */
}


/****************************************************************************
  MarkUnusedAndEmptyLayers( ... )

     1. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY
        and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI
        and there is NO succeeding non-empty layer then mark the 1st element
        of the layer DIFV_BOTH_EMPTY; this layerr will be omitted.

     2. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY
        and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI
        and there IS a succeeding non-empty layer then mark the 1st element
        of the layer DIFV_IS_EMPTY and all other elements DIFV_BOTH_EMPTY;
        only the first empty segment of this layerr will be output.

     3. If NOT all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY
        and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI
        and the 1st element of the layer is DIFV_BOTH_EMPTY then mark it
        DIFV_IS_EMPTY; it will be output as empty (except M layer).

****************************************************************************/
int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] )
{
    int i, nLayer, sBits, nFirstSegm;
#define nFirstFmlSegm   DIFS_f_FORMULA
#define nFirstIsoSegm   DIFS_i_IATOMS
     /* FI */
    nLayer = DIFL_FI;
    nFirstSegm = nFirstIsoSegm;
    sBits = 0;
    for (i = 0; i < DIFS_idf_LENGTH; i++)
    {
        sBits |= sDifSegs[nLayer][i];
    }
    if (!( sBits & DIFV_OUTPUT_OMIT_F ))
    {
        /* Omit the FI layer */
        memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH ); /* djb-rwth: memset_s C11/Annex K variant? */
    }
    else
    {
        if (sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY ||
                !( sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F ))
        {
            sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY;
        }
    }

        /* MI */
    nLayer = DIFL_MI;
    nFirstSegm = nFirstIsoSegm;
    sBits = 0;
    for (i = 0; i < DIFS_idf_LENGTH; i++)
    {
        sBits |= sDifSegs[nLayer][i];
    }
    if (!( sBits & DIFV_OUTPUT_OMIT_F ))
    {
        /* Omit the MI layer */
        memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH ); /* djb-rwth: memset_s C11/Annex K variant? */
    }
    else
    {
        if (sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY ||
          !( sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F ))
        {
            sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY;
        }
    }

    /* F */
    nLayer = DIFL_F;
    nFirstSegm = nFirstFmlSegm;
    sBits = 0;
    for (i = 0; i < DIFS_idf_LENGTH; i++)
    {
        sBits |= sDifSegs[nLayer][i];
    }
    if (!( sBits & DIFV_OUTPUT_OMIT_F ) &&
         sDifSegs[DIFL_FI][nFirstIsoSegm] == DIFV_BOTH_EMPTY)
    {
        /* Omit the F layer: no non-iotopic and no isotopic segments */
        memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH ); /* djb-rwth: memset_s C11/Annex K variant? */
    }
    else
    {    /* do not omit fixed-H layer */
        if (sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY ||
             !( sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F ))
        {
            sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY;
        }
    }

    /* M -- leave as it is */

    return 0;

#undef nFirstFmlSegm
#undef nFirstIsoSegm
}


/****************************************************************************/
int CompareInchiStereo( INChI_Stereo *Stereo1,
                        INCHI_MODE nFlags1,
                        INChI_Stereo *Stereo2,
                        INCHI_MODE nFlags2 )
{
    int i, num, ret;
    if (Stereo2 && Stereo1)
    {
        /*  compare stereogenic bonds */
        num = inchi_min( Stereo2->nNumberOfStereoBonds, Stereo1->nNumberOfStereoBonds );
        for (i = 0; i < num; i++)
        {
            if ((ret = (int) Stereo2->nBondAtom1[i] - (int) Stereo1->nBondAtom1[i])) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) Stereo2->nBondAtom2[i] - (int) Stereo1->nBondAtom2[i])) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) Stereo2->b_parity[i] - (int) Stereo1->b_parity[i])) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
        }
        if ((ret = (int) Stereo2->nNumberOfStereoBonds - (int) Stereo1->nNumberOfStereoBonds)) /* djb-rwth: addressing LLVM warning */
        {
            return ret;
        }

        /*  compare stereogenic atoms */
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
        if (( ( nFlags1 | nFlags2 ) & ( INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO ) ) &&
             1 == Stereo2->nNumberOfStereoCenters &&
             1 == Stereo1->nNumberOfStereoCenters)
        {
            ; /*  do not compare single stereocenters in case of relative stereo */
        }
        else
#endif
        {
            num = inchi_min( Stereo2->nNumberOfStereoCenters, Stereo1->nNumberOfStereoCenters );
            for (i = 0; i < num; i++)
            {
                if ((ret = (int) Stereo2->nNumber[i] - (int) Stereo1->nNumber[i])) /* djb-rwth: addressing LLVM warning */
                {
                    return ret;
                }
                if ((ret = (int) Stereo2->t_parity[i] - (int) Stereo1->t_parity[i])) /* djb-rwth: addressing LLVM warning */
                {
                    return ret;
                }
            }
            if ((ret = (int) Stereo2->nNumberOfStereoCenters - (int) Stereo1->nNumberOfStereoCenters)) /* djb-rwth: addressing LLVM warning */
                return ret;
            /*  compare stereo-abs-is-inverted flags  for non-relative, non-racemic */
            if (!( ( nFlags1 | nFlags2 ) & ( INCHI_FLAG_RAC_STEREO | INCHI_FLAG_REL_STEREO ) ))
            {
                if ((ret = ( Stereo2->nCompInv2Abs < 0 ) - ( Stereo1->nCompInv2Abs < 0 ))) /* djb-rwth: addressing LLVM warning */
                {
                    return ret;
                }
            }
        }
    }
    else
    {
        if (Stereo2 && ( Stereo2->nNumberOfStereoBonds > 0 || Stereo2->nNumberOfStereoCenters > 0
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                         && /*  do not compare single stereocenters in case of relative stereo */
                         !( ( nFlags2 & ( INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO ) ) &&
                            1 == Stereo2->nNumberOfStereoCenters
                            )
#endif
                         ))
        {
            return 1;
        }
        else
        {
            if (Stereo1 && ( Stereo1->nNumberOfStereoBonds > 0 || Stereo1->nNumberOfStereoCenters > 0
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                             && /*  do not compare single stereocenters in case of relative stereo */
                             !( ( nFlags1 & ( INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO ) ) &&
                                1 == Stereo1->nNumberOfStereoCenters
                                )
#endif
                             ))
            {
                return -1;
            }
        }
    }

    return 0;
}


/****************************************************************************
  CompINChI2( ... )

    Sorting in descending order:
        return -1 if *p1 > *p2, return +1 if *p1 < *p2

****************************************************************************/
int CompINChI2( const INCHI_SORT *p1,
                const INCHI_SORT *p2,
                int bTaut,
                int bCompareIsotopic )
{
    int ret, num, i, num_H1, num_H2;

    const INChI *i1 = NULL; /* tautomeric if exists, otherwise non-tautomeric */
    const INChI *i2 = NULL; /* tautomeric if exists, otherwise non-tautomeric */

    int   n1;               /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */
    int   n2;               /* TAUT_YES if tautomeric i2 exists, otherwise TAUT_NON */

    const INChI *i1n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */
    const INChI *i2n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */

    /*const INChI *i1t = NULL;*/ /* temp for i1 if both tautomeric AND non-tautomeric exist */
    /*const INChI *i2t = NULL;*/ /* temp for i2 if both tautomeric AND non-tautomeric exist */

    /* INChI_Stereo *Stereo1, *Stereo2; */

    n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms ) ? TAUT_YES : TAUT_NON;
    n2 = ( p2->pINChI[TAUT_YES] && p2->pINChI[TAUT_YES]->nNumberOfAtoms ) ? TAUT_YES : TAUT_NON;

    i1 = p1->pINChI[n1];
    i1n = ( n1 == TAUT_YES && p1->pINChI[TAUT_NON] &&
           p1->pINChI[TAUT_NON]->nNumberOfAtoms ) ? p1->pINChI[TAUT_NON] : (const INChI *) NULL;

    i2 = p2->pINChI[n2];
    i2n = ( n2 == TAUT_YES && p2->pINChI[TAUT_NON] &&
          p2->pINChI[TAUT_NON]->nNumberOfAtoms ) ? p2->pINChI[TAUT_NON] : (const INChI *) NULL;

    /* non-deleted-non-empty < deleted < empty */
    if (i1 && !i2)
        return -1;   /* non-empty is the smallest (first) */
    if (!i1 && i2)
        return 1;
    if (!i1 && !i2)
        return 0;
    if (i1->bDeleted && !i2->bDeleted)
        return 1;    /* deleted is the largest (last) among non-empty */
    if (!i1->bDeleted && i2->bDeleted)
        return -1;

    num_H1 = num_H2 = 0;

    /* do not compare terminal H */
    if ((ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ))) /* djb-rwth: addressing LLVM warning */
    {
        return ret;  /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */
    }

    /*********************************************************
            compare non-isotopic non-tautomeric part
     *********************************************************/

    /* compare number of atoms (excluding terminal H) */
    if ((ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms)) /* djb-rwth: addressing LLVM warning */
        return ret; /*  more atoms first */

    /*  compare elements  (excluding terminal H) */
    num = i1->nNumberOfAtoms;
    for (i = 0; i < num; i++)
    {
        /* should always be equal if Hill formulas are same */
        if ((ret = (int) i2->nAtom[i] - (int) i1->nAtom[i])) /* djb-rwth: addressing LLVM warning */
            return ret; /* greater periodic number first */
    }

    /**********************************************************
        compare connection tables
    ***********************************************************/
    if ((ret = i2->lenConnTable - i1->lenConnTable)) /* djb-rwth: addressing LLVM warning */
        return ret; /* longer connection table first */
    num = i2->lenConnTable;
    for (i = 0; i < num; i++)
    {
        if ((ret = (int) i2->nConnTable[i] - (int) i1->nConnTable[i])) /* djb-rwth: addressing LLVM warning */
            return ret; /* greater connection table first */
    }

    /*********************************************************
      compare compare total number of H (inverse: H3 < H2 )
    **********************************************************/
    if ((ret = num_H2 - num_H1)) /* djb-rwth: addressing LLVM warning */
    {
        return ret;
    }

    /*********************************************************
      compare non-tautomeric num_H: N < NH3 < NH2 < NH
    **********************************************************/
    num = i1->nNumberOfAtoms;
    for (i = 0; i < num; i++)
    {
        if (i2->nNum_H[i] != i1->nNum_H[i])
        {
            return !i2->nNum_H[i] ? 1 :  /* no H first */
                !i1->nNum_H[i] ? -1 :
                (int) i2->nNum_H[i] - (int) i1->nNum_H[i];
        }
    }

    /*********************************************************
         compare non-isotopic tautomeric part
     *********************************************************/
    if ((ret = CompareTautNonIsoPartOfINChI( i1, i2 ))) /* djb-rwth: addressing LLVM warning */
    {
        return ret;
    }
    /*
    if ( ret = i2->lenTautomer - i1->lenTautomer )
        return ret;
    num = inchi_min( i2->lenTautomer, i1->lenTautomer );
    for ( i = 0; i < num; i ++ ) {
        if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] )
           return ret;
    }
    */

    /*********************************************************
     *                                                       *
     *  at this point both components are either tautomeric  *
     *  or non-tautomeric                                    *
     *                                                       *
     *********************************************************/

    /*********************************************************
       non-tautomeric "fixed H" specific
     *********************************************************/
    if (TAUT_NON == bTaut && ( (i1n && i1n->nNum_H_fixed) || (i2n && i2n->nNum_H_fixed) )) /* djb-rwth: addressing LLVM warning */
    {
        /* first, compare non-tautomeric chem. formulas -- they may be different */
        const char *f1 = ( i1n /*&& i1n->nNum_H_fixed*/ ) ? i1n->szHillFormula : i1->szHillFormula;
        const char *f2 = ( i2n /*&& i2n->nNum_H_fixed*/ ) ? i2n->szHillFormula : i2->szHillFormula;
        if (f1 && f2 && ( ret = CompareHillFormulas( f1, f2 ) ))
        {
            return ret;
        }
        /* secondly, compare fixed-H distribution */
        if (i1n && i1n->nNum_H_fixed && i2n && i2n->nNum_H_fixed)
        {
            num = inchi_min( i1n->nNumberOfAtoms, i2n->nNumberOfAtoms );
            for (i = 0; i < num; i++)
            {
                if (i2n->nNum_H_fixed[i] != i1n->nNum_H_fixed[i])
                {
                    return !i2n->nNum_H_fixed[i] ? 1 : /* no fixed H first */
                        !i1n->nNum_H_fixed[i] ? -1 :
                        (int) i2n->nNum_H_fixed[i] - (int) i1n->nNum_H_fixed[i];
                }
            }
            if ((ret = (int) i2n->nNumberOfAtoms - (int) i1n->nNumberOfAtoms)) /* djb-rwth: addressing LLVM warning */
            {
                return ret; /* should not happen <BRKPT> */
            }
        }
        else
        {
            if (i1n && i1n->nNum_H_fixed)
            {
                num = i1n->nNumberOfAtoms;
                for (i = 0; i < num; i++)
                {
                    /* added 2004-05-04 */
                    if (i1n->nNum_H_fixed[i])
                    {
                        return -1; /* i1n->nNum_H_fixed[i] > 0? -1:1;*/
                    }
                }
                /* p1 is tautomeric, p2 is not tautomeric; this must have been detected earlier */
                /*return -1;*/ /* has fixed H first *//* <BRKPT> */ /* removed 2004-05-04 */
            }
            else
            {
                num = i2n->nNumberOfAtoms;
                for (i = 0; i < num; i++)
                {
                    /* added 2004-05-04 */
                    if (i2n->nNum_H_fixed[i])
                    {
                        return 1; /* i2n->nNum_H_fixed[i] > 0? 1:-1;*/
                    }
                }
                /* p2 is tautomeric, p1 is not tautomeric; this must have been detected earlier */
                /*return 1; */ /* has fixed H first *//* <BRKPT> */ /* removed 2004-05-04 */
            }
        }
    }

    /*************************************************************************
              if requested non-tautomeric comparison then
              prepare to compare non-taut non-isotopic stereo, etc.
     *************************************************************************/
    if (TAUT_NON == bTaut)
    {
        if (i1n)
        {
            /*i1t = i1;*/
            i1 = i1n;
        }
        if (i2n)
        {
            /*i2t = i2;*/
            i2 = i2n;
        }
    }

    /*********************************************************
        compare non-isotopic stereo
     *********************************************************/
    ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags );
    if (ret)
    {
        return ret;
    }
    /*******************************************************
        do not switch back to tautomeric i1, i2
     *******************************************************/
    /* -- how to switch back --
    if ( i1t ) {
        i1  = i1t;
        i1t = NULL;
    }
    if ( i2t ) {
        i2  = i2t;
        i2t = NULL;
    }
    */

    /******************************************************
         compare isotopic non-tautomeric part
     ******************************************************/
    if (bCompareIsotopic)
    {
        if ((ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms)) /* djb-rwth: addressing LLVM warning */
        {
            return ret;
        }
        num = i1->nNumberOfIsotopicAtoms;
        /*  compare isotopic atoms */
        for (i = 0; i < num; i++)
        {
            if ((ret = (int) i2->IsotopicAtom[i].nAtomNumber - (int) i1->IsotopicAtom[i].nAtomNumber)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) i2->IsotopicAtom[i].nIsoDifference - (int) i1->IsotopicAtom[i].nIsoDifference)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
        }

        /* compare isotopic H */
        /* if tautomeric comparison mode then here are compared only non-tautomeric H */
        for (i = 0; i < num; i++)
        {
            if ((ret = (int) i2->IsotopicAtom[i].nNum_T - (int) i1->IsotopicAtom[i].nNum_T)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) i2->IsotopicAtom[i].nNum_D - (int) i1->IsotopicAtom[i].nNum_D)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) i2->IsotopicAtom[i].nNum_H - (int) i1->IsotopicAtom[i].nNum_H)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
        }

        /*****************************************************
             compare isotopic tautomeric part
         *****************************************************/
        if ((ret = i2->nNumberOfIsotopicTGroups - i1->nNumberOfIsotopicTGroups)) /* djb-rwth: addressing LLVM warning */
            return ret;
        num = i1->nNumberOfIsotopicTGroups;
        for (i = 0; i < num; i++)
        {
            if ((ret = (int) i2->IsotopicTGroup[i].nTGroupNumber - (int) i1->IsotopicTGroup[i].nTGroupNumber)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) i2->IsotopicTGroup[i].nNum_T - (int) i1->IsotopicTGroup[i].nNum_T)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) i2->IsotopicTGroup[i].nNum_D - (int) i1->IsotopicTGroup[i].nNum_D)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
            if ((ret = (int) i2->IsotopicTGroup[i].nNum_H - (int) i1->IsotopicTGroup[i].nNum_H)) /* djb-rwth: addressing LLVM warning */
            {
                return ret;
            }
        }

        /****************************************************
            compare isotopic stereo
         ****************************************************/
        ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags );
        if (ret)
        {
            return ret;
        }
    }

    /**********************************************************
        compare charges: non-charged first, then in order of
        ascending charges (negative first)
    ***********************************************************/
    if (i2->nTotalCharge && i1->nTotalCharge)
    {
        /*  both are charged; smaller charges first */
        ret = (int) i1->nTotalCharge - (int) i2->nTotalCharge;
        return ret;
    }
    if ((ret = ( i1->nTotalCharge ? 1 : 0 ) - ( i2->nTotalCharge ? 1 : 0 ))) /* djb-rwth: addressing LLVM warning */
    {
        /*  only one is charged; uncharged first */
        return ret;
    }
    /* stable sort */
    /*ret = p1->ord_number - p2->ord_number;*/

    return ret;
}


/****************************************************************************/
int CompINChINonTaut2( const void *p1, const void *p2 )
{
    int ret;
    ret = CompINChI2( (const INCHI_SORT *) p1, (const INCHI_SORT *) p2, TAUT_NON, 1 );
#if ( CANON_FIXH_TRANS == 1 )
    if (!ret)
    {
        /* to obtain canonical transposition 2004-05-10 */
        ret = CompINChI2( (const INCHI_SORT *) p1, (const INCHI_SORT *) p2, TAUT_YES, 1 );
    }
#endif
    if (!ret)
    {
        /* stable sort */
        ret = ( (const INCHI_SORT *) p1 )->ord_number - ( (const INCHI_SORT *) p2 )->ord_number;
    }

    return ret;
}


/****************************************************************************/
int CompINChITaut2( const void *p1, const void *p2 )
{
    int ret;
    ret = CompINChI2( (const INCHI_SORT *) p1, (const INCHI_SORT *) p2, TAUT_YES, 1 );
#if ( CANON_FIXH_TRANS == 1 )
    if (!ret)
    {
        /* to obtain canonical transposition 2004-05-10 */
        ret = CompINChI2( (const INCHI_SORT *) p1, (const INCHI_SORT *) p2, TAUT_NON, 1 );
    }
#endif
    if (!ret)
    {
        /* stable sort */
        ret = ( (const INCHI_SORT *) p1 )->ord_number - ( (const INCHI_SORT *) p2 )->ord_number;
    }

    return ret;
}


/****************************************************************************
 mystrrev( ... )
    NB:
    strrev from K&R is not in ANSI-compatible C library
****************************************************************************/
void mystrrev( char *p )
{
    char c, *q = p;
    while (*q++)
    {
        ;
    }
    q -= 2; /*  pointer to the last character */
    while (p < q)
    {
        c = *q;  /*  swap */
        *q-- = *p;
        *p++ = c;
    }
}


/****************************************************************************
Find DFS order for CT(canon. numbers and Hs) output
****************************************************************************/
typedef struct tagOrderStruct
{
    AT_NUMB   *m_gDfs4CT_nDfsNumber;
    AT_NUMB   *m_gDfs4CT_nNumDescendants;
    int        m_gDfs4CT_nCurrentAtom;
} OrderStruct;


/****************************************************************************/
static int CompareDfsDescendants4CT( const void *a1, const void *a2, void *p )
{
    OrderStruct *os = (OrderStruct *) p;
    int neigh1 = ( int )*(const AT_RANK*) a1;
    int neigh2 = ( int )*(const AT_RANK*) a2;
    if (neigh1 > MAX_ATOMS)
    {
        if (neigh2 > MAX_ATOMS)
        {
            return 0;
        }
        return 1;
    }
    else if (neigh2 > MAX_ATOMS)
    {
        return -1;
    }
    else
    {
        AT_RANK nCurDfsNumber = os->m_gDfs4CT_nDfsNumber[os->m_gDfs4CT_nCurrentAtom];
        int nDesc1 = nCurDfsNumber > os->m_gDfs4CT_nDfsNumber[neigh1] ?
            0 : (int) os->m_gDfs4CT_nNumDescendants[neigh1];
        int nDesc2 = nCurDfsNumber > os->m_gDfs4CT_nDfsNumber[neigh2] ?
            0 : (int) os->m_gDfs4CT_nNumDescendants[neigh2];
        int ret;
        if ((ret = nDesc1 - nDesc2)) /* djb-rwth: addressing LLVM warning */
        {
            return ret;
        }
        return  (int) neigh1 - (int) neigh2; /*  canon. numbers difference */
    }
}


/****************************************************************************/
AT_NUMB *GetDfsOrder4CT( CANON_GLOBALS *pCG,
                         AT_NUMB *LinearCT,
                         int nLenCT,
                         S_CHAR *nNum_H,
                         int num_atoms,
                         int nCtMode )
{
    AT_NUMB    *nStackAtom = NULL;
    int         nTopStackAtom = -1;
    AT_NUMB    *nNumDescendants = NULL; /*  number of descendants incl. closures and the atom itself */
    AT_NUMB    *nDfsNumber = NULL;
    S_CHAR     *cNeighNumb = NULL;
    NEIGH_LIST *nl = NULL;
    AT_NUMB     nDfs;
    int         i, j, u, k, start, num_rings, nTotOutputStringLen;
    AT_NUMB    *nOutputString = NULL, cDelim;
    int         bCtPredecessors = ( nCtMode & CT_MODE_PREDECESSORS );
    OrderStruct os;

    /*  allocate arrays */
    nStackAtom = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nStackAtom[0] ) );
    nNumDescendants = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nNumDescendants[0] ) );
    nDfsNumber = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nDfsNumber[0] ) );
    cNeighNumb = (S_CHAR  *) inchi_malloc( num_atoms * sizeof( cNeighNumb[0] ) );
    nl = CreateNeighListFromLinearCT( LinearCT, nLenCT, num_atoms );

    /*  check allocation */
    if (!nStackAtom || !nNumDescendants || !nDfsNumber || !cNeighNumb || !nl)
    {
        /* ret = CT_OUT_OF_RAM; */ /*  program error */ /*   <BRKPT> */
        goto exit_function;
    }
    if (bCtPredecessors)
    {
        start = 0;
    }
    else
    {
        /*  find DFS start vertex (atom) */
        for (i = 1, start = 0; i < num_atoms; i++)
        {
            if (nl[i][0] < nl[start][0])
            { /*  index = nRank-1 */
                start = i;
            }
        }
    }

    /*
      vertex information:
        1. Number of (forward edges) + (back edges, first visit -- ring closures): nl[i][0]
        2. Number of vertices traversed from this vertex, including the vertex:    nNumDescendants[i]
        3. Each edge information:
           a. forward edge (0) or back edge (1) indicator: nDfsNumber[i] > nDfsNumber[neigh]
           b. neighbor at another end of the edge neigh = nl[i][k+1], k < i

        Total per edge: 2 + 2*(number of edges)
    */

    /* DFS initiation */
    u = start; /* start atom */
    nDfs = 0;
    nTopStackAtom = -1;
    memset( nDfsNumber, 0, num_atoms * sizeof( nDfsNumber[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    memset( nNumDescendants, 0, num_atoms * sizeof( nNumDescendants[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    memset( cNeighNumb, 0, num_atoms * sizeof( cNeighNumb[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    /*  push the start atom on the stack */
    nDfsNumber[u] = ++nDfs;
    if (bCtPredecessors)
    {
        nNumDescendants[u] = 0; /* atom #1 has no predecessor */
    }
    else
    {
        nNumDescendants[u] = 1; /* count itself as a descendant */
    }
    nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
    /* nNumStartChildren = 0; */
    num_rings = 0;

    /* DFS */
    do
    {
        /* advance */
        while (i = (int) nStackAtom[nTopStackAtom], j = (int) cNeighNumb[i] + 1, (int) nl[i][0] >= j)
            /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/

            /* replaced due to missing sequence point; undefined behavior, pointed by Geoffrey Hutchison */
        {
            cNeighNumb[i] ++;
            u = (int) nl[i][j]; /*  jth neighbor of the vertex i */
            if (!nDfsNumber[u])
            {
                /* tree edge, 1st visit -- advance */
                /* put unexplored vertex u on the stack for further examination */
                nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
                nDfsNumber[u] = ++nDfs;
                if (bCtPredecessors)
                {
                    nNumDescendants[u] = i + 1; /* predecessor's rank */
                }
                else
                {
                    nNumDescendants[u] ++; /* count atom u as its descendant */
                }
            }
            else
            {
                if (nTopStackAtom && u != (int) nStackAtom[nTopStackAtom - 1] &&
                    /* back edge: u is not a predecessor of i */
                    nDfsNumber[u] < nDfsNumber[i])
                {
                    /* Back edge, 1st visit: u is an ancestor of i (ring closure) */
                    if (!bCtPredecessors)
                    {
                        nNumDescendants[i] ++; /* count closures as descendants */
                    }
                    num_rings++;          /* count ring closures */
                }
                else
                {
                    nl[i][j] = MAX_ATOMS + 1; /* back edge, 2nd visit: mark as deleted */
                }
            }
        }
        cNeighNumb[i] = 0; /* all neighbors of the ith atom have been
                              traversed; resore the neighbor counter */
                              /* back up */
        if (!bCtPredecessors && nTopStackAtom /* that is, i != start */)
        {
            u = (int) nStackAtom[nTopStackAtom - 1]; /* predecessor of i */
            nNumDescendants[u] += nNumDescendants[i]; /* add descendants */
        }
    } while (--nTopStackAtom >= 0);

    /* Sort the neighbors in ascending order so that:
       primary key   = number of descendants in the DFS tree; closure neighbor is 0
       secondary key = canonical number (here vertex number = canonical number - 1)
     */

    os.m_gDfs4CT_nDfsNumber = nDfsNumber;
    os.m_gDfs4CT_nNumDescendants = nNumDescendants;
    os.m_gDfs4CT_nCurrentAtom = -1;

    /* sorting; deleted will be the last neighbors */
    for (i = 0; i < num_atoms; i++)
    {
        if (nl[i][0] > 1)
        {
            os.m_gDfs4CT_nCurrentAtom = i;
            insertions_sort( &os, &nl[i][1], nl[i][0], sizeof( nl[i][1] ), CompareDfsDescendants4CT );
        }
        /* reduce number of neighbors to exclude deleted */
        for (k = 0; k < nl[i][0] && nl[i][k + 1] <= MAX_ATOMS; k++)
        {
            ;
        }
        nl[i][0] = k;
    }

    nTotOutputStringLen = 3 * ( num_atoms + num_rings + 1 ); /*  last 3 elements are a 'zero termination' */

    if (bCtPredecessors)
    {
        if ((nOutputString = (AT_RANK *) inchi_calloc( nTotOutputStringLen, sizeof( nOutputString[0] ) ))) /* djb-rwth: addressing LLVM warning */
        {
            cDelim = '-';
            for (u = 0, k = -3; u < num_atoms; u++)
            {
                k += 3;
                if (k + 6 > nTotOutputStringLen)
                {
                    goto exit_error;  /* program error */
                }
                nOutputString[k] = nNumDescendants[u] ? nNumDescendants[u] : MAX_ATOMS + 1;
                nOutputString[k + 1] = nNum_H ? 16 + nNum_H[u] : 0;
                nOutputString[k + 2] = k ? ',' : '\0';
                for (j = 1; j <= nl[u][0] && nDfsNumber[u] > nDfsNumber[i = nl[u][j]]; j++)
                {
                    /* closures */
                    k += 3;
                    if (k + 6 > nTotOutputStringLen)
                    {
                        goto exit_error;  /* program error */
                    }
                    nOutputString[k] = i + 1;  /* closure */
                    nOutputString[k + 1] = 0;
                    nOutputString[k + 2] = cDelim;
                }
            }
        }
    }
    else
    {
        if (nNumDescendants)
        {  /* do not need anymore */
            inchi_free( nNumDescendants );
            nNumDescendants = NULL;
        }

        /*
            the output string contains:
              (num_atoms) atoms for the DFS (spanning) tree
              (num_atoms-1) delimiters for the DFS (spanning) tree
              1 character for each atom that has 1 terminal hydrogen atoms
              2 characters  for each atom that has 2-9 terminal hydrogen atoms
              3 characters  for each atom that has 10-99 terminal hydrogen atoms, etc.
              (num_rings) atoms for the ring closures
              (num_rings) delimiters for the ring closures
        */

        if ((nOutputString = (AT_RANK *) inchi_calloc( nTotOutputStringLen, sizeof( nOutputString[0] ) ))) /* djb-rwth: addressing LLVM warning */
        {
            u = start; /*  start atom */
            nTopStackAtom = -1;
            memset( cNeighNumb, 0, num_atoms * sizeof( cNeighNumb[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            /*  push the start atom on the stack */
            nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
            /*  output the starting atom */
            k = 0;
            nOutputString[k] = u + 1;
            nOutputString[k + 1] = nNum_H ? 16 + nNum_H[u] : 0;
            nOutputString[k + 2] = '\0';

            do
            {
                /* advance */
                while (i = (int) nStackAtom[nTopStackAtom], j = (int) cNeighNumb[i] + 1, i < num_atoms && (int) nl[i][0] >= j) /* djb-rwth: additional condition to avoid buffer overruns */
                    /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/
                    /* replaced due to missing sequence point; undefined behavior, reported by Geoffrey Hutchison */
                {
                    k += 3;
                    if (k + 6 > nTotOutputStringLen)
                    {
                        goto exit_error;  /* program error */
                    }
                    cNeighNumb[i] ++;
                    u = (int) nl[i][j]; /* neighbor */

                    /* output neighbor's canonical number */
                    nOutputString[k] = u + 1;

                    if (nDfsNumber[u] > nDfsNumber[i])
                    {
                        /* tree edge, 1st visit -- advance */
                        /* put 'unexplored' vertex u on the stack */
                        nStackAtom[++nTopStackAtom] = (AT_NUMB) u;

                        /* output neighbor's number of H */
                        nOutputString[k + 1] = nNum_H ? 16 + nNum_H[u] : 0;
                    }
                    else
                    {
                        nOutputString[k + 1] = 0;
                    }
                    /* output a delimiter preceding the neighbor */
                    if (1 < nl[i][0])
                    {
                        if (j == 1)
                        {
                            cDelim = '(';
                        }
                        else
                        {
                            if (j == nl[i][0])
                            {
                                cDelim = ')';
                            }
                            else
                            {
                                cDelim = ',';
                            }
                        }
                    }
                    else
                    {
                        cDelim = '-';
                    }
                    nOutputString[k + 2] = cDelim;
                }
                cNeighNumb[i] = 0;

                /* back up: nothing else to do */
            } while (--nTopStackAtom >= 0);
        }
    }
    goto exit_function;

exit_error:
    if (nOutputString)
    {
        inchi_free( nOutputString );
        nOutputString = NULL;
    }

exit_function:
    if (nStackAtom)
    {
        inchi_free( nStackAtom );
    }
    if (nNumDescendants)
    {
        inchi_free( nNumDescendants );
    }
    if (nDfsNumber)
    {
        inchi_free( nDfsNumber );
    }
    if (cNeighNumb)
    {
        inchi_free( cNeighNumb );
    }
    if (nl)
    {
        FreeNeighList( nl );
    }

    return nOutputString;
}


/****************************************************************************/
int GetInpStructErrorType( INPUT_PARMS *ip,
                           int err,
                           char *pStrErrStruct,
                           int num_inp_atoms )
{
    if (err && err == 9)
    {
        return _IS_ERROR; /*  sdfile bypassed to $$$$ */
    }
    if (err && err < 30)
    {
        return _IS_FATAL;
    }
    if (num_inp_atoms <= 0 || err)
    {
        if (98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure)
        {
            return _IS_WARNING;
        }
        return _IS_ERROR;
    }
    if (pStrErrStruct[0])
    {
        return _IS_WARNING;
    }

    return _IS_OKAY;
}


/****************************************************************************/
int ProcessStructError( INCHI_IOSTREAM *out_file,
                        INCHI_IOSTREAM *log_file,
                        char *pStrErrStruct,
                        int nErrorType,
                        long num_inp,
                        INPUT_PARMS *ip )
{
    int b_ok;
#ifdef TARGET_LIB_FOR_WINCHI
    int bPlainText = ( ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT ) &&
        ( ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW );
#else
    int bPlainText = 0;
#endif
    if (!bPlainText)
    {
        return nErrorType;
    }

    /*  Fatal error, Error, Warning */
    if (nErrorType)
    {
        b_ok = 0; /* to make VC++ happy */
        if (bPlainText)
        {
            if (!( b_ok = OutputINChIPlainError( out_file, pStrErrStruct, nErrorType ) ))
            {
                inchi_ios_eprint( log_file, "Cannot create message for error (structure #%ld.%s%s%s%s) Terminating.\n",
                                                            num_inp, SDF_LBL_VAL( ip->pSdfLabel, ip->pSdfValue ) );
            }
            else
            {
                inchi_ios_print( out_file, "\n" ); /* add a blank line after the WINCHI Window message */
            }
        }
        return b_ok ? nErrorType : _IS_FATAL;
    }

    return nErrorType;
}


#if ( READ_INCHI_STRING == 1 ) /*  { */
/****************************************************************************/
int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */,
                                INChI_Stereo *s2 /* input InChI */
)
{
    if (s1 == NULL && s2 == NULL)
    {
        return 0;
    }
    if (( s1 == NULL ) ^ ( s2 == NULL ))
    {
        INChI_Stereo *s = s1 ? s1 : s2;
        if (s->nNumberOfStereoCenters || s->nNumberOfStereoBonds)
        {
            return 20; /* Diff: Missing Stereo */
        }
        else
        {
            return 0;
        }
    }

    if (s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters)
    {
        return 21;      /* Diff: Number of sp3 stereocenters */
    }
    if (s1->nNumberOfStereoCenters > 0)
    {
        if (memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters * sizeof( s1->nNumber[0] ) ))
        {
            return 22;  /* Diff: sp3 stereocenter locations */
        }
        if (memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters * sizeof( s1->t_parity[0] ) ))
        {
            return 23;  /* Diff: sp3 stereocenter parities */
        }
        if (s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs)
        {
            return 24;  /* Diff: sp3 inversion */
        }
        /*
        if ( s1->nNumberInv && s2->nNumberInv ) {
            if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) )
                return 25;
            if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) )
                return 26;
            if ( s1->nCompInv2Abs != s2->nCompInv2Abs ||
                 s1->bTrivialInv  != s2->bTrivialInv ) {
                return 27;
            }
        } else
        if ( s1->nNumberInv || s2->nNumberInv ) {
            return 28;
        }
        */
    }
    if (s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds)
    {
        return 25;      /* Diff: Number of stereobonds */
    }
    if (s1->nNumberOfStereoBonds > 0)
    {
        if (memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds * sizeof( s1->nBondAtom1[0] ) ))
        {
            return 26; /* Diff: Stereobond 1st atom locations */
        }
        if (memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds * sizeof( s1->nBondAtom2[0] ) ))
        {
            return 27; /* Diff: Stereobond 2nd atom locations */
        }
        if (memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds * sizeof( s1->b_parity[0] ) ))
        {
            return 28; /* Diff: Stereobond parities */
        }
    }

    return 0;
}


/****************************************************************************/
int CompareReversedStereoINChI2( 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;

    /* djb-rwth: redundant part of the condition corrected; fixing a NULL pointer dereference */
    if (s1 && s2 && ( 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] ) ) ))
    {
        num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0;
        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 |= IDIF_SC_PARITY;
        }
        if (num_in1_only)
        {
            if (num_extra_undf)
            {
                ret |= IDIF_SC_EXTRA_UNDF;
            }
            if (num_in1_only != num_extra_undf)
            {
                ret |= IDIF_SC_EXTRA;
            }
        }
        if (num_in2_only)
        {
            if (num_miss_undf)
            {
                ret |= IDIF_SC_MISS_UNDF;
            }
            if (num_in2_only != num_miss_undf)
            {
                ret |= IDIF_SC_MISS;
            }
        }
    }
    if (s1 && s2 && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs)
    {
        ret |= IDIF_SC_INV;
    }

    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 |= IDIF_SB_PARITY;
        }
        if (num_in1_only)
        {
            if (num_extra_undf)
            {
                ret |= IDIF_SB_EXTRA_UNDF;
            }
            if (num_in1_only != num_extra_undf)
            {
                ret |= IDIF_SB_EXTRA;
            }
        }
        if (num_in2_only)
        {
            if (num_miss_undf)
            {
                ret |= IDIF_SB_MISS_UNDF;
            }
            if (num_in2_only != num_miss_undf)
            {
                ret |= IDIF_SB_MISS;
            }
        }
    }

    return ret;
}


/****************************************************************************/
int CompareReversedINChI( INChI *i1 /* InChI from reversed struct */,
                          INChI *i2 /* input InChI */,
                          INChI_Aux *a1,
                          INChI_Aux *a2 )
{
    int ret;

    if (i1 == NULL && i2 == NULL)
    {
        return 0;
    }
    if (( i1 == NULL ) ^ ( i2 == NULL ))
    {
        return 1; /* Diff: Missing InChI */
    }

    if (i1->nErrorCode == i2->nErrorCode)
    {
        if (i1->nErrorCode)
        {
            return 0;
        }
    }
    else
    {
        return 2; /* Diff: Error codes */
    }
    if (i1->bDeleted != i2->bDeleted)
    {
        return 1; /* Diff: Missing InChI */
    }
    if (i1->nNumberOfAtoms != i2->nNumberOfAtoms)
    {
        return 3;  /* Diff: Num. atoms */
    }

    if (i1->nNumberOfAtoms > 0)
    {
        if (memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms * sizeof( i1->nAtom[0] ) ))
        {
            return 4; /* Diff: Elements */
        }
        if (strcmp( i1->szHillFormula, i2->szHillFormula ))
        {
            return 7; /* Diff: Hill Formulas */
        }
        if (memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms * sizeof( i1->nNum_H[0] ) ))
        {
            if (i1->lenConnTable > 1 || i2->lenConnTable > 1)
            {
                return 5; /* Diff: H Locations (mobile H present) */
            }
            else
            {
                return 6; /* Diff: H Locations (no mobile H) */
            }
        }

        /* fixed H */
        if (i1->nNum_H_fixed || i2->nNum_H_fixed)
        {
            int bHasFixedH1 = 0, bHasFixedH2 = 0, i, j1, j2;
            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++;
                    }
                }
            }

            /* count the differences */
            j1 = j2 = 0;
            if (bHasFixedH1 && !bHasFixedH2)
            {
                for (i = 0; i < i1->nNumberOfAtoms; i++)
                {
                    if (i1->nNum_H_fixed[i] > 0)
                    {
                        j1++;
                    }
                    else
                        if (i1->nNum_H_fixed[i] < 0)
                        {
                            j2++;
                        }
                }
                return 18; /* Diff: Extra Fixed-H */
            }
            else
            {
                if (!bHasFixedH1 && bHasFixedH2)
                {
                    for (i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i++)
                    {
                        if (0 > i2->nNum_H_fixed[i])
                        {
                            j1++;
                        }
                        else
                            if (0 < i2->nNum_H_fixed[i])
                            {
                                j2++;
                            }
                    }
                    return 19; /* Diff: 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])
                            {
                                j1++;
                            }
                            else
                            {
                                if (i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i])
                                {
                                    j2++;
                                }
                            }
                        }
                    }
                }
            }
            ret = ( j1 && j2 ) ? 20 : j1 ? 18 : j2 ? 19 : 0;
            if (ret)
            {
                return ret; /* 20 => Diff: NotEql Fixed-H */
                            /* 19 => Diff: Missed Fixed-H (i1 has less) */
                            /* 18 => Diff: Extra Fixed-H  (i1 has more) */
            }
        }
    }

    if (i1->lenConnTable != i2->lenConnTable)
    {
        return 8; /* Diff: Connections length */
    }
    if (i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable * sizeof( i1->nConnTable[0] ) ))
    {
        return 9; /* Diff: Connections */
    }

    /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */
    if (i1->lenTautomer != i2->lenTautomer && ( i1->lenTautomer > 1 || i2->lenTautomer > 1 ))
    {
        return 10; /* Diff: Mobile groups length */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */
    }
    if (( i1->lenTautomer > 1 && i2->lenTautomer > 1 ) &&
         memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer * sizeof( i1->nTautomer[0] ) ))
    {
        return 11; /* Diff: Mobile groups */
    }

    if (i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms)
    {
        return 12; /* Diff: Isotopic atoms number */
    }
    if (i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms * sizeof( i1->IsotopicAtom[0] ) ))
    {
        return 13; /* Diff: Isotopic atoms */
    }
    if (i1->nTotalCharge != i2->nTotalCharge)
    {
        return 14; /* Diff: Charge */
    }

    /*
        if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups )
        {
            return 14;
        }
        if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) )
        {
            return 15;
            }
    */

    if (a1 && a2)
    {
        if (a1->nNumRemovedProtons != a2->nNumRemovedProtons)
        {
            return 16; /* Diff: Number of removed protons */
        }
        if (memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof( a1->nNumRemovedIsotopicH ) ))
        {
            return 17; /* Diff: Removed isotopic 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;}
            }
        }
    */

    /* ret = 20..31 => 40..51 */
    if ((ret = CompareReversedStereoINChI( i1->Stereo, i2->Stereo ))) /* djb-rwth: addressing LLVM warning */
    {
        return ret + 20;
    }
    /* ret = 40..51 => 60..71 */

    if (!i2->StereoIsotopic && i2->Stereo && i1->StereoIsotopic &&
         0 < ( i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) &&
         0 == CompareReversedStereoINChI( i1->StereoIsotopic, i2->Stereo ))
    {
        /* InChI from reversed structure does not contain fully duplicated isotopic stereo */
        ;
    }
    else
    {
        if ((ret = CompareReversedStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ))) /* djb-rwth: addressing LLVM warning */
        {
            return ret + 40;
        }
    }

    return 0;
}


/****************************************************************************/
int CompareIcr( ICR *picr1,
                ICR *picr2,
                INCHI_MODE *pin1,
                INCHI_MODE *pin2,
                INCHI_MODE mask )
{
    int nNumExtraBits1 = 0, nNumExtraBits2 = 0, bit1, bit2;
    INCHI_MODE Flg1 = picr1->flags, Flg2 = picr2->flags, cur_bit = 1, in1, in2;
    int i, ret;

    /* compare flags */
    in1 = in2 = 0;
    for (i = 0; Flg1 || Flg2; i++, Flg1 >>= 1, Flg2 >>= 1, cur_bit <<= 1)
    {
        if (!( mask & cur_bit ))
        {
            continue;
        }
        bit1 = Flg1 & 1;
        bit2 = Flg2 & 1;
        if (bit1 && !bit2)
        {
            in1 |= 1 << i;
            nNumExtraBits1++;
        }
        else
        {
            if (!bit1 && bit2)
            {
                in2 |= 1 << i;
                nNumExtraBits2++;
            }
        }
    }
    if (nNumExtraBits1 && !nNumExtraBits2)
    {
        ret = 1;
    }
    else
    {
        if (!nNumExtraBits1 && nNumExtraBits2)
        {
            ret = -1;
        }
        else
        {
            if (!in1 && !in2)
            {
                ret = 0;
            }
            else
            {
                ret = 2; /* compare produced undefined results */
            }
        }
    }

    if (pin1)
    {
        *pin1 = in1;
    }
    if (pin2)
    {
        *pin2 = in2;
    }

    /* more detailed compare not implemented */

    return ret;
}


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

    *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 |= IDIF_PROBLEM; /* one InChI exists while another doesn't */
        goto exit_function;
    }

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

    if (i1->nNumberOfAtoms != i2->nNumberOfAtoms)
    {
        ret |= IDIF_NUM_AT;
        goto exit_function;
    }
    if (i1->nNumberOfAtoms > 0)
    {
        if (memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms * sizeof( i1->nAtom[0] ) ))
        {
            ret |= IDIF_ATOMS;
            goto exit_function;
        }
        /* IDIF_NON_TAUT_H,  IDIF_MORE_FH, IDIF_LESS_FH */
        if (memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms * sizeof( i1->nNum_H[0] ) ))
        {
            ret |= IDIF_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 |= IDIF_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 |= IDIF_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 ? IDIF_MORE_FH : 0 ) | ( j2 ? IDIF_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 |= IDIF_NUM_EL;
        goto exit_function;
    }
    if (num_H1 > num_H2)
    {
        ret |= IDIF_MORE_H;
    }
    if (num_H1 < num_H2)
    {
        ret |= IDIF_LESS_H;
    }
    if (i1->lenConnTable != i2->lenConnTable)
    {
        ret |= IDIF_CON_LEN;
        goto exit_function;
    }
    else
    {
        if (i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable * sizeof( i1->nConnTable[0] ) ))
        {
            ret |= IDIF_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 |=  IDIF_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 |= IDIF_NO_TAUT;
    }
    else
    {
        if (n1 && !n2)
        {
            ret |= IDIF_WRONG_TAUT;
        }
        else
        {
            if (n1 == 1 && n2 > 1)
            {
                ret |= IDIF_SINGLE_TG;
            }
            else
            {
                if (n1 > 1 && n2 == 1)
                {
                    ret |= IDIF_MULTIPLE_TG;
                }
                else
                {
                    if (n1 != n2)
                    {
                        ret |= IDIF_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 = -1; /* 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_RANK( pe1, num1 );
        insertions_sort_AT_RANK( pe2, num2 );
        /* compare */
        /*
        if ( num1 < num2 ) {
            ret |= IDIF_LESS_TG_ENDP;
        } else
        if ( num1 > num2 ) {
            ret |= IDIF_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[j2])
                { /* BC: fixed, was pe2[j1] 2006-03-27 */
                    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 |= IDIF_EXTRA_TG_ENDP;
        }
        if (num_in2_only)
        {
            ret |= IDIF_MISS_TG_ENDP;
        }
        if (!num_in1_only && !num_in2_only && num_eq)
        {
            ; /* same t-groups endpoints */
        }
        else
        {
            ret |= IDIF_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 |= IDIF_TG;
    }

    if (i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms)
    {
        ret |= IDIF_NUM_ISO_AT;
    }
    else
    {
        if (i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms * sizeof( i1->IsotopicAtom[0] ) ))
        {
            ret |= IDIF_ISO_AT;
        }
    }
    if (i1->nTotalCharge != i2->nTotalCharge)
    {
        ret |= IDIF_CHARGE;
    }
    if (a1 && a1->nNumRemovedProtons && ( !a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons ))
    {
        ret |= IDIF_REM_PROT;
    }
    if (a1 && ( !a2 ||
        a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] ||
        a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] ||
        a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2] ))
    {
        ret |= IDIF_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 |= CompareReversedStereoINChI2( Stereo1, Stereo2, picr );

exit_function:

    picr->flags = ret;

    return ret;
}
#endif  /* } READ_INCHI_STRING */


/****************************************************************************/
int  Create_INChI( CANON_GLOBALS *pCG,
                   INCHI_CLOCK *ic,
                   INPUT_PARMS *ip,
                   INChI **ppINChI,
                   INChI_Aux **ppINChI_Aux,
                   ORIG_ATOM_DATA *orig_inp_data,
                   inp_ATOM *inp_at,
                   INP_ATOM_DATA *out_norm_data[2],
                   int num_inp_at,
                   INCHI_MODE nUserMode,
                   INCHI_MODE *pbTautFlags,
                   INCHI_MODE *pbTautFlagsDone,
                   struct tagInchiTime *ulMaxTime,
                   T_GROUP_INFO *ti_out,
                   char *pStrErrStruct )
{
/*
#define NON_TAUT 0
#define TAUT     1
*/
    int nebend = 0, *ebend = NULL;

    sp_ATOM  *at[TAUT_NUM]; /* at[0]=>non-tautomeric, at[1]=>tautomeric */
    int                       i, n1, n2, num_atoms, num_at_tg, num_removed_H, num_removed_H_taut = 0, ret = 0, ret2 = 0;
    INCHI_MODE                 nMode = 0;
    T_GROUP_INFO              vt_group_info;
    T_GROUP_INFO              vt_group_info_orig;
    T_GROUP_INFO * /*const*/  t_group_info = &vt_group_info;
    T_GROUP_INFO * /*const*/  t_group_info_orig = &vt_group_info_orig;

    CANON_STAT  CS, CS2;
    CANON_STAT *pCS = &CS;
    CANON_STAT *pCS2 = &CS2;  /*  save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */

    ATOM_SIZES  s[TAUT_NUM];

    BCN Bcn;
    BCN *pBCN = &Bcn;

    int bHasIsotopicAtoms = 0;
    int bMayHaveStereo = 0;
    int num_taut_at = 0;

    inp_ATOM *out_at = NULL;     /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /*  = {out_norm_nontaut_at, out_norm_taut_at} ; */
    INChI     *pINChI = NULL;      /* added initialization 2006-03 */
    INChI_Aux *pINChI_Aux = NULL;  /* added initialization 2006-03 */
    int        bPointedEdgeStereo = ( ( TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags ) ? PES_BIT_POINT_EDGE_STEREO : 0 )
        | ( ( TG_FLAG_PHOSPHINE_STEREO    & *pbTautFlags ) ? PES_BIT_PHOSPHINE_STEREO : 0 )
        | ( ( TG_FLAG_ARSINE_STEREO       & *pbTautFlags ) ? PES_BIT_ARSINE_STEREO : 0 )
        | ( ( TG_FLAG_FIX_SP3_BUG         & *pbTautFlags ) ? PES_BIT_FIX_SP3_BUG : 0 );
    INCHI_MODE bTautFlags = ( *pbTautFlags     & ( ~(INCHI_MODE) TG_FLAG_ALL_TAUTOMERIC ) );
    INCHI_MODE bTautFlagsDone = ( *pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */ );
#if ( bRELEASE_VERSION == 0 )
    int bExtract = 0; /*  EXTR_HAS_ATOM_WITH_DEFINED_PARITY; */
#endif

/* */
    int bFixIsoFixedH = 0;
    int bFixTermHChrg = 0;

    int LargeMolecules = ip->bLargeMolecules;
    /* djb-rwth: removing redundant variables */

    /*    vABParityUnknown holds actual value of an internal constant signifying
        unknown parity: either the same as for undefined parity (default==standard)
        or a specific one (non-std; requested by SLUUD switch).                 */
    int vABParityUnknown = AB_PARITY_UNDF;
    if (0 != ( nUserMode & REQ_MODE_DIFF_UU_STEREO ))
    {
        /* Make labels for unknown and undefined stereo different */
        vABParityUnknown = AB_PARITY_UNKN;
    }

    /* djb-rwth: removing redundant code */

#if ( FIX_ISO_FIXEDH_BUG == 1 )
    if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags)
        bFixIsoFixedH = 1;
#endif
#if ( FIX_TERM_H_CHRG_BUG == 1 )
    if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags)
        bFixTermHChrg = 1;
#endif

#ifdef FIX_SRU_CYCLIZING_PS_BONDS_IN_BNS
    /* Polymer related */
    if (orig_inp_data && orig_inp_data->polymer && orig_inp_data->polymer->n > 0 && orig_inp_data->polymer->valid)
    {
        int j, jj;
        nebend = 0;
        for (j = 0; j < orig_inp_data->polymer->n; j++)
            if (orig_inp_data->polymer->units[j]->cyclized == 1)
                nebend++;
        if (nebend)
        {
            nebend *= 2;
            ebend = inchi_calloc( 2 * nebend, sizeof( int ) );
            if (!ebend)
            {
                ret = CT_OUT_OF_RAM; goto exit_function;
            }
            jj = 0;
            for (j = 0; j < orig_inp_data->polymer->n; j++)
            {
                if (orig_inp_data->polymer->units[j]->cyclized == 1)
                {
                    ebend[jj] = orig_inp_data->polymer->units[j]->end_atom1;
                    ebend[jj + 1] = orig_inp_data->polymer->units[j]->end_atom2;
                    jj += 2;
                }
            }
        }
    }
#endif

    memset( s, 0, sizeof( s ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    if (pBCN)
    {
        memset( pBCN, 0, sizeof( pBCN[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    }
    memset( t_group_info, 0, sizeof( *t_group_info ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    memset( t_group_info_orig, 0, sizeof( *t_group_info_orig ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    /*norm_at[TAUT_NON] = out_norm_data[TAUT_NON]->at; *//* output normalized non-tautomeric component */
    /*norm_at[TAUT_YES] = out_norm_data[TAUT_YES]->at; *//* output normalized tautomeric component */
    /*norm_at_fixed_bonds[TAUT_NON] = NULL;*/
    /*norm_at_fixed_bonds[TAUT_YES] = out_norm_data[TAUT_YES]->at_fixed_bonds;*/
    for (i = 0; i < TAUT_NUM; i++)
    {
        if (out_norm_data[i]->at)
        {
            if (!( at[i] = (sp_ATOM  *) inchi_malloc( num_inp_at * sizeof( *at[0] ) ) ))
            {
                ret = -1;
            }
        }
        else
        {
            at[i] = NULL;
        }
    }
    
    if ((!out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at)
         || !inp_at || ret) /* djb-rwth: addressing LLVM warning */
    {
        ret = -1;
        goto exit_function;
    }

    /* the first struct to process: tautomeric if exists else non-tautomeric */
    out_at = out_norm_data[TAUT_YES]->at ? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at;
    /* copy the input structure to be normalized to the buffer for the normalization data */
    memcpy(out_at, inp_at, num_inp_at * sizeof(out_at[0]));
    /*  tautomeric groups setting */
    t_group_info->bIgnoreIsotopic = 0;   /*  include tautomeric group isotopic info in MarkTautomerGroups() */
    t_group_info->bTautFlags = *pbTautFlags;
    t_group_info->bTautFlagsDone = *pbTautFlagsDone;
    t_group_info->t_group = NULL; /* djb-rwth: fixing oss-fuzz issue #70475 */

    /*
        Preprocess the structure
        (here THE NUMBER OF ATOMS MAY BE REDUCED)
    */

    /*  ??? Ambiguity: H-D may become HD or DH
        (that is, H+implicit D or D+implicit H)    */
    if (TG_FLAG_H_ALREADY_REMOVED & bTautFlags)
    {
        INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at ? out_norm_data[TAUT_YES] :
            out_norm_data[TAUT_NON]->at ? out_norm_data[TAUT_NON] : NULL;
        if (out_norm_data1)
        {
            num_at_tg =
                num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H;
            num_removed_H = out_norm_data1->num_removed_H;
            t_group_info->tni.nNumRemovedExplicitH = num_removed_H;
        }
        else
        {
            ret = -1;
            goto exit_function;
        }
    }
    else
    {
        num_at_tg = num_atoms = remove_terminal_HDT( num_inp_at, out_at, bFixTermHChrg );
        num_removed_H = num_inp_at - num_atoms;
        t_group_info->tni.nNumRemovedExplicitH = num_removed_H;
        add_DT_to_num_H( num_atoms, out_at );
    }
    /*fix_odd_things( num_atoms, out_at );*/
#if ( FIND_RING_SYSTEMS == 1 )
    MarkRingSystemsInp( out_at, num_atoms, 0 );
#endif
    /*  duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */
    if (out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at)
    {
        memcpy(out_norm_data[TAUT_YES]->at, out_at, num_inp_at * sizeof(out_at[0]));
    }
    if (out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at)
    {
        memcpy(out_norm_data[TAUT_YES]->at_fixed_bonds, out_at, num_inp_at * sizeof(out_at[0]));
    }
    if (out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at)
    {
        memcpy(out_norm_data[TAUT_NON]->at, out_at, num_inp_at * sizeof(out_at[0]));
    }

    /*
      ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal
      set stereo parities in taut_at[], non_taut_at[]
      obtain max. lenghts of the name stereo parts
      Ignore absence/presence of isotopic stereo for now
      mark isotopic atoms
    */
    if (out_norm_data[TAUT_YES]->at && at[TAUT_YES])
    {
        /* final normalization of possibly tautomeric structure */
        ret = mark_alt_bonds_and_taut_groups( ic, pCG,
                                              out_norm_data[TAUT_YES]->at,
                                              out_norm_data[TAUT_YES]->at_fixed_bonds,
                                              num_atoms, ulMaxTime, t_group_info,
                                              NULL, NULL,
                                              nebend, ebend );
        if (ret < 0)
        {
            goto exit_function;/*  out of RAM or other normalization problem */
        }
        num_taut_at = ret; /* number of atoms without removed H? */
        num_removed_H_taut = t_group_info->tni.nNumRemovedExplicitH;
        out_norm_data[TAUT_YES]->num_at = num_atoms + num_removed_H_taut; /* protons might have been removed */
        out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut;
        out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons;
        for (i = 0; i < NUM_H_ISOTOPES; i++)
        {
            out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] +=
                t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/;
            out_norm_data[TAUT_YES]->num_iso_H[i] +=
                t_group_info->num_iso_H[i];
        }
        /* mark deleted isolated tautomeric H(+) */
        if (num_taut_at == 1 &&
             out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON &&
             t_group_info && t_group_info->tni.nNumRemovedProtons == 1)
        {
            out_norm_data[TAUT_YES]->bDeleted = 1;
            FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds );
        }
        else
        {
            if (( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) &&
                    out_norm_data[TAUT_YES]->at_fixed_bonds)
            {
                out_norm_data[TAUT_YES]->bTautPreprocessed = 1;
            }
        }

        /*
            if ( !(t_group_info->tni.bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT & ~FLAG_PROTON_SINGLE_REMOVED)) &&
                 out_norm_data[TAUT_YES]->at_fixed_bonds) {
                 FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds );
            }
        */

        /*out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut;*/

        out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags;
        out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone;
        out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags;

        /* create internal sp_ATOM at[] out of out_norm_data[]->at */
        inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, at[TAUT_YES] );

        /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */
        ret = set_stereo_parity( pCG, out_norm_data[TAUT_YES]->at, at[TAUT_YES],
                                 num_taut_at, num_removed_H_taut,
                                 &s[TAUT_YES].nMaxNumStereoAtoms,
                                 &s[TAUT_YES].nMaxNumStereoBonds,
                                 nUserMode, bPointedEdgeStereo,
                                 vABParityUnknown, ip->bLooseTSACheck, ip->bStereoAtZz );

#if ( bRELEASE_VERSION == 0 )
        if (0 < ret)
        {
            bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY;
        }
        if (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT)
        {
            bExtract |= EXTR_TAUT_TREATMENT_CHARGES;
        }
#endif

        if (RETURNED_ERROR( ret ))
        {
            goto exit_function; /*  stereo bond error */
        }

        s[TAUT_YES].bMayHaveStereo =
            ( s[TAUT_YES].nMaxNumStereoAtoms ||
             s[TAUT_YES].nMaxNumStereoBonds );

        /*
            mark isotopic atoms and atoms that have non-tautomeric
            isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T)
        */

        s[TAUT_YES].num_isotopic_atoms =
            set_atom_iso_sort_keys( num_taut_at,
                                    at[TAUT_YES],
                                    t_group_info,
                                    &s[TAUT_YES].bHasIsotopicTautGroups );

        /*
            Prepare tautomeric (if no tautomerism found then prepare non-tautomeric)
            structure for canonicalizaton:

                remove t-groups that have no H,
                remove charges from t-groups if requested
                renumber t-groups and find final t_group_info->num_t_groups
                add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[]
                calculate length of the t-group part of the connection table
         */

        s[TAUT_YES].nLenLinearCTTautomer = CountTautomerGroups( at[TAUT_YES], num_taut_at, t_group_info );

        if (RETURNED_ERROR( s[TAUT_YES].nLenLinearCTTautomer ))
        {
            /* added error treatment 9-11-2003 */
            ret = s[TAUT_YES].nLenLinearCTTautomer;
            goto exit_function;
            /*  error has happened; no breakpoint here
            s[TAUT_YES].nLenLinearCTTautomer = 0;
            */
        }

        else if (s[TAUT_YES].nLenLinearCTTautomer > 0)
        {
            num_at_tg = num_taut_at + t_group_info->num_t_groups;
            /*  ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */
            make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ );
            /*  mark isotopic tautomer groups: calculate t_group->iWeight */
            s[TAUT_YES].nLenLinearCTIsotopicTautomer = set_tautomer_iso_sort_keys( t_group_info );
            if (s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0)
            {
                /* ??? -error cannot happen- error has happened; no breakpoint here */
                s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0;
            }
            out_norm_data[TAUT_YES]->bTautomeric = s[TAUT_YES].nLenLinearCTTautomer;
        }

        /*  new variable: s[TAUT_YES].nLenCT introduced 7-22-2002 */
        GetCanonLengths( num_taut_at, at[TAUT_YES], &s[TAUT_YES], t_group_info );
    }

    if (out_norm_data[TAUT_NON]->at &&
         out_norm_data[TAUT_YES]->at &&
         at[TAUT_NON] &&
         !s[TAUT_YES].nLenLinearCTTautomer)
    {
        /* the structure is non-tautomeric: use tautomeric treatment results only for it */
        inchi_free( at[TAUT_NON] );
        at[TAUT_NON] = NULL;
    }

    else if (!out_norm_data[TAUT_NON]->at &&
         out_norm_data[TAUT_YES]->at &&
         !at[TAUT_NON] &&
         at[TAUT_YES] &&
         !s[TAUT_YES].nLenLinearCTTautomer)
    {
        /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */
        out_norm_data[TAUT_YES]->bTautomeric = 0;
    }

    else if (out_norm_data[TAUT_NON]->at && at[TAUT_NON])
    {
        /* the structure needs non-tautomeric treatment:
        final normalization of non-tautomeric structure */
        ret = mark_alt_bonds_and_taut_groups( ic, pCG,
                                              out_norm_data[TAUT_NON]->at,
                                              NULL,
                                              num_atoms,
                                              ulMaxTime,
                                              NULL,
                                              &bTautFlags,
                                              &bTautFlagsDone,
                                              nebend, ebend );
        if (ret < 0)
        {
            goto exit_function;  /*  out of RAM or other normalization problem */
        }
        out_norm_data[TAUT_NON]->num_at = num_atoms + num_removed_H;
        out_norm_data[TAUT_NON]->num_removed_H = num_removed_H;
        out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags;
        out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone;
        out_norm_data[TAUT_NON]->bNormalizationFlags = 0;

        /* create internal sp_ATOM at[] out of out_norm_data[]->at */
        inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, at[TAUT_NON] );

        /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */
        ret = set_stereo_parity( pCG, out_norm_data[TAUT_NON]->at,
                                 at[TAUT_NON], num_atoms, num_removed_H,
                                 &s[TAUT_NON].nMaxNumStereoAtoms,
                                 &s[TAUT_NON].nMaxNumStereoBonds, nUserMode,
                                 bPointedEdgeStereo, vABParityUnknown,
                                 ip->bLooseTSACheck, ip->bStereoAtZz );
#if ( bRELEASE_VERSION == 0 )
        if (0 < ret)
        {
            bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY;
        }
#endif
        if (RETURNED_ERROR( ret ))
        {
            goto exit_function; /*  stereo bond error */
        }
        s[TAUT_NON].bMayHaveStereo = ( s[TAUT_NON].nMaxNumStereoAtoms ||
                                      s[TAUT_NON].nMaxNumStereoBonds );

        /*
         * mark isotopic atoms and atoms that have non-tautomeric
         * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T)
         */
        s[TAUT_NON].num_isotopic_atoms = set_atom_iso_sort_keys( num_atoms, at[TAUT_NON], NULL, NULL );
        GetCanonLengths( num_atoms, at[TAUT_NON], &s[TAUT_NON], NULL );
        out_norm_data[TAUT_NON]->bTautomeric = 0;
    }

    /* common  */
    bMayHaveStereo = s[TAUT_YES].bMayHaveStereo || s[TAUT_NON].bMayHaveStereo;
    bHasIsotopicAtoms = s[TAUT_NON].num_isotopic_atoms > 0 || s[TAUT_NON].bHasIsotopicTautGroups > 0 ||
        s[TAUT_YES].num_isotopic_atoms > 0 || s[TAUT_YES].bHasIsotopicTautGroups > 0;
    if (bFixIsoFixedH)
    {
        /* 2008-03-21 DT */
        bHasIsotopicAtoms = bHasIsotopicAtoms
            ||
            (s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info &&
            ( (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0]) ||
              (1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1]) ||
              (2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2])
            )); /* djb-rwth: addressing LLVM warning */
    }
    bHasIsotopicAtoms = bHasIsotopicAtoms
        ||
        (s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info &&
        ( t_group_info->bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ) )); /* djb-rwth: addressing LLVM warning */

    /* default mode */
    if (!( nUserMode & REQ_MODE_DEFAULT ))
    {
        /*  default */
        nUserMode |= REQ_MODE_DEFAULT;
    }

    /* adjust the mode to the reality */
    if (( nUserMode & REQ_MODE_ISO ) && !bHasIsotopicAtoms)
    {
        nUserMode ^= REQ_MODE_ISO;
        nUserMode |= REQ_MODE_NON_ISO;  /*  at least one is needed */
    }
    if (( nUserMode & REQ_MODE_STEREO ) && ( nUserMode & REQ_MODE_ISO ))
    {
        nUserMode |= REQ_MODE_ISO_STEREO;
    }
    if (( nUserMode & REQ_MODE_STEREO ) && !( nUserMode & REQ_MODE_NON_ISO ))
    {
        nUserMode ^= REQ_MODE_STEREO;
    }
    if (!bMayHaveStereo)
    {
        if (nUserMode & REQ_MODE_STEREO)
            nUserMode ^= REQ_MODE_STEREO;
        if (nUserMode & REQ_MODE_ISO_STEREO)
            nUserMode ^= REQ_MODE_ISO_STEREO;
    }

    if (( nUserMode & REQ_MODE_BASIC ) &&
        ( !out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] ||
            !ppINChI_Aux[TAUT_NON] || !at[TAUT_NON] ))
    {
        nUserMode ^= REQ_MODE_BASIC;
    }
    if (( nUserMode & REQ_MODE_TAUT ) &&
        ( !out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] ||
            !ppINChI_Aux[TAUT_YES] || !at[TAUT_YES] ))
    {
        nUserMode ^= REQ_MODE_TAUT;
    }

    switch ((int) nUserMode & ( REQ_MODE_BASIC | REQ_MODE_TAUT ))
    {
        case REQ_MODE_BASIC:
            n1 = TAUT_NON;
            n2 = TAUT_NON;
            break;
        case REQ_MODE_TAUT:
            n1 = TAUT_YES;
            n2 = TAUT_YES;
            break;
        case ( REQ_MODE_BASIC | REQ_MODE_TAUT ):
            n1 = TAUT_NON;
            n2 = TAUT_YES;
            break;
        default:
            ret = -3;
            goto exit_function; /*  program error: inconsistent nUserMode or missing taut/non-taut allocation */ /*   <BRKPT> */
    }

    /*
        Obtain all non-stereo canonical numberings
    */

    if (( nUserMode & REQ_MODE_NON_ISO ) && !( nUserMode & REQ_MODE_ISO ))
    {
        /* added for special non-isotopic test mode 2004-10-04 */
        if (t_group_info)
        {
            t_group_info->bIgnoreIsotopic = 1;
            if (t_group_info->nIsotopicEndpointAtomNumber)
            {
                t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min( 1, t_group_info->nIsotopicEndpointAtomNumber[0] );
            }
            memset( t_group_info->num_iso_H, 0, sizeof( t_group_info->num_iso_H ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            memset( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof( t_group_info->tni.nNumRemovedProtonsIsotopic ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            t_group_info->bTautFlagsDone &= ~( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE );
        }
        for (i = 0; i < TAUT_NUM; i++)
        {
            s[i].bHasIsotopicTautGroups = 0;
            s[i].bIgnoreIsotopic = 1;
            s[i].nLenIsotopic = 0;
            s[i].nLenIsotopicEndpoints = 0;
            s[i].nLenLinearCTIsotopicTautomer = 0;
            s[i].num_isotopic_atoms = 0;
        }
        bHasIsotopicAtoms = 0;
    }

    ret = GetBaseCanonRanking( ic, num_atoms, num_at_tg, at,
                               t_group_info, s, pBCN, ulMaxTime,
                               pCG, bFixIsoFixedH, LargeMolecules );

    if (ret < 0)
    {
        goto exit_function; /*  program error */
    }
#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 )
    /* Debug only: find whether canonical equivalence is different from equitable partition */
    if (bCanonIsFinerThanEquitablePartition( num_atoms, at[n1], pBCN->ftcn[TAUT_NON].nSymmRankCt ))
    {
        bExtract |= EXTR_CANON_NE_EQUITABLE;
    }
#endif

    /* added for special non-isotopic test mode 2004-10-04 */
    if (!pBCN->ftcn[n1].PartitionCt.Rank)
    {
        n1 = ALT_TAUT( n1 );
    }
    if (!pBCN->ftcn[n2].PartitionCt.Rank)
    {
        n2 = ALT_TAUT( n2 );
    }
    if (n1 > n2)
    {
        ret = CT_TAUCOUNT_ERR;
        goto exit_function; /*  program error */
    }

    /*
        Obtain stereo canonical numberings
    */

    for (i = n2; i >= n1 && !RETURNED_ERROR( ret ); i--)
    {
        memset( pCS, 0, sizeof( *pCS ) ); /* djb-rwth: memset_s C11/Annex K variant? */

        switch (i)
        {
            case TAUT_NON:
                /*  non-tautomeric */
                /* djb-rwth: removing redundant code */
                nMode = ( s[i].nLenLinearCTTautomer == 0 ) ? CANON_MODE_CT : CANON_MODE_TAUT;
                nMode |= ( bHasIsotopicAtoms && ( nUserMode & REQ_MODE_ISO ) ) ? CANON_MODE_ISO : 0;
                nMode |= ( s[TAUT_NON].bMayHaveStereo && ( nUserMode & REQ_MODE_STEREO ) ) ? CANON_MODE_STEREO : 0;
                nMode |= ( bHasIsotopicAtoms && s[TAUT_NON].bMayHaveStereo && ( nUserMode & REQ_MODE_ISO_STEREO ) ) ? CANON_MODE_ISO_STEREO : 0;
                nMode |= ( nUserMode & REQ_MODE_NOEQ_STEREO ) ? CMODE_NOEQ_STEREO : 0;
                nMode |= ( nUserMode & REQ_MODE_REDNDNT_STEREO ) ? CMODE_REDNDNT_STEREO : 0;
                nMode |= ( nUserMode & REQ_MODE_NO_ALT_SBONDS ) ? CMODE_NO_ALT_SBONDS : 0;
                if (( nMode & CANON_MODE_STEREO ) == CANON_MODE_STEREO ||
                    ( nMode & CANON_MODE_ISO_STEREO ) == CANON_MODE_ISO_STEREO)
                {
                    nMode |= ( nUserMode & REQ_MODE_RELATIVE_STEREO ) ? CMODE_RELATIVE_STEREO : 0;
                    nMode |= ( nUserMode & REQ_MODE_RACEMIC_STEREO ) ? CMODE_RACEMIC_STEREO : 0;
                    nMode |= ( nUserMode & REQ_MODE_SC_IGN_ALL_UU ) ? CMODE_SC_IGN_ALL_UU : 0;
                    nMode |= ( nUserMode & REQ_MODE_SB_IGN_ALL_UU ) ? CMODE_SB_IGN_ALL_UU : 0;
                }
                if ((ret = AllocateCS( pCS, num_atoms, num_atoms, s[TAUT_NON].nLenCT, s[TAUT_NON].nLenCTAtOnly,
                    s[TAUT_NON].nLenLinearCTStereoDble, s[TAUT_NON].nMaxNumStereoBonds,
                    s[TAUT_NON].nLenLinearCTStereoCarb, s[TAUT_NON].nMaxNumStereoAtoms,
                    0, 0, s[TAUT_NON].nLenIsotopic, nMode, pBCN ))) /* djb-rwth: addressing LLVM warning */
                {
                    goto exit_function;
                }
                *pCS2 = *pCS;
                break;
            case TAUT_YES: /*  tautomeric */
                /* djb-rwth: removing redundant code */
                nMode = ( s[i].nLenLinearCTTautomer == 0 ) ? CANON_MODE_CT : CANON_MODE_TAUT;
                nMode |= ( bHasIsotopicAtoms && ( nUserMode & REQ_MODE_ISO ) ) ? CANON_MODE_ISO : 0;
                nMode |= ( s[TAUT_YES].bMayHaveStereo && ( nUserMode & REQ_MODE_STEREO ) ) ? CANON_MODE_STEREO : 0;
                nMode |= ( bHasIsotopicAtoms && s[TAUT_YES].bMayHaveStereo && ( nUserMode & REQ_MODE_ISO_STEREO ) ) ? CANON_MODE_ISO_STEREO : 0;
                nMode |= ( nUserMode & REQ_MODE_NOEQ_STEREO ) ? CMODE_NOEQ_STEREO : 0;
                nMode |= ( nUserMode & REQ_MODE_REDNDNT_STEREO ) ? CMODE_REDNDNT_STEREO : 0;
                nMode |= ( nUserMode & REQ_MODE_NO_ALT_SBONDS ) ? CMODE_NO_ALT_SBONDS : 0;
                if (( nMode & CANON_MODE_STEREO ) == CANON_MODE_STEREO ||
                    ( nMode & CANON_MODE_ISO_STEREO ) == CANON_MODE_ISO_STEREO)
                {
                    nMode |= ( nUserMode & REQ_MODE_RELATIVE_STEREO ) ? CMODE_RELATIVE_STEREO : 0;
                    nMode |= ( nUserMode & REQ_MODE_RACEMIC_STEREO ) ? CMODE_RACEMIC_STEREO : 0;
                    nMode |= ( nUserMode & REQ_MODE_SC_IGN_ALL_UU ) ? CMODE_SC_IGN_ALL_UU : 0;
                    nMode |= ( nUserMode & REQ_MODE_SB_IGN_ALL_UU ) ? CMODE_SB_IGN_ALL_UU : 0;
                }
                if ((ret = AllocateCS( pCS, num_atoms, num_at_tg, s[TAUT_YES].nLenCT, s[TAUT_YES].nLenCTAtOnly,
                    s[TAUT_YES].nLenLinearCTStereoDble, s[TAUT_YES].nMaxNumStereoBonds,
                    s[TAUT_YES].nLenLinearCTStereoCarb, s[TAUT_YES].nMaxNumStereoAtoms,
                    s[TAUT_YES].nLenLinearCTTautomer, s[TAUT_YES].nLenLinearCTIsotopicTautomer,
                    s[TAUT_YES].nLenIsotopic, nMode, pBCN ))) /* djb-rwth: addressing LLVM warning */
                {
                    goto exit_function;
                }
                *pCS2 = *pCS;
                break;
        }

        /* 2009-12-05 */
        nMode |= ( nUserMode & REQ_MODE_DIFF_UU_STEREO ) ? REQ_MODE_DIFF_UU_STEREO : 0;
        /* 2009-12-05 */

        /*  settings */
        pCS->lNumDecreasedCT = -1;
        pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST ? 2 : 0;  /*  2 => special mode */
        pCS->bIgnoreIsotopic = !( ( s[TAUT_NON].num_isotopic_atoms ||
            s[TAUT_YES].num_isotopic_atoms ||
            s[TAUT_YES].bHasIsotopicTautGroups ) ||
            ( nUserMode & REQ_MODE_NON_ISO ) ||
                                               !( nUserMode & REQ_MODE_ISO ) );

        if (( nUserMode & REQ_MODE_NON_ISO ) && !( nUserMode & REQ_MODE_ISO ))
        {
            pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */
        }

        if (i == TAUT_YES)
        {
            /* tautomeric */
            pCS->t_group_info = t_group_info; /*  ??? make a copy or reuse ???  */
            pCS->t_group_info->bIgnoreIsotopic = !( s[TAUT_YES].bHasIsotopicTautGroups ||
                ( nUserMode & REQ_MODE_NON_ISO ) ||
                                                   !( nUserMode & REQ_MODE_ISO ) );
            if (( nUserMode & REQ_MODE_NON_ISO ) && !( nUserMode & REQ_MODE_ISO ))
            {
                pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */
            }
        }

        pCS->ulTimeOutTime = pBCN->ulTimeOutTime;
        /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) ===========
         *
         *  Mode      Bits       Description
         *   '0' c    0          Only one connection table canonicalization
         *   '1' C    1          Recalculate CT using fixed nSymmRank
         *   '2' i    1|2        Isotopic canonicalization (internal)
         *   '3' I    1|2|4      Isotopic canonicalization (output)
         *   '4' s    1|8        Stereo canonicalization
         *   '5' S    1|2|4|16   Stereo isotopic canonicalization
         *   '6' A    1|2|4|8|16 Output All
         */

        /*
            The last canonicalization step
        */

        if (pBCN)
        {
            /* USE_CANON2 == 1 */
            pCS->NeighList = NULL;
            pCS->pBCN = pBCN;

            ret = Canon_INChI( ic,
                               num_atoms,
                               i ? num_at_tg : num_atoms,
                               at[i], pCS,
                               pCG,
                               nMode, i );
        }
        else
        {
            /* old way */
            pCS->NeighList = CreateNeighList( num_atoms,
                                               i ? num_at_tg : num_atoms,
                                               at[i],
                                               pCS->bDoubleBondSquare,
                                               pCS->t_group_info );
            pCS->pBCN = NULL;

            ret = Canon_INChI( ic,
                               num_atoms,
                               i ? num_at_tg : num_atoms,
                               at[i], pCS,
                               pCG,
                               nMode, i );
        }

        pINChI = ppINChI[i];      /* pointers to already allocated still empty InChI */
        pINChI_Aux = ppINChI_Aux[i];

        if (ret <= 0)
        {
            /*
                Failure in Canon_INChI()
            */
            pINChI->nErrorCode = ret;
            pINChI_Aux->nErrorCode = ret;
        }
        else
        {
            /*
                Success Canon_INChI()

                save canonicalization results in
                pINChI and pINChI_Aux
            */
            pINChI->nErrorCode = 0;
            pINChI_Aux->nErrorCode = 0;
            pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted;
            pINChI_Aux->nCanonFlags = pCS->nCanonFlags;
            pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags;
            pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone;
            pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags;

            /*  may return an error or a warning */
            ret = FillOutINChI( pINChI, pINChI_Aux,
                               num_atoms, i ? num_at_tg : num_atoms,
                               i ? num_removed_H_taut : num_removed_H, at[i],
                               out_norm_data[i]->at, pCS,
                               pCG,
                               i, nUserMode,
                               pStrErrStruct, ip->bNoWarnings );

            if (RETURNED_ERROR( ret ))
            {
                /* Failure in FillOutINChI() */
                pINChI->nErrorCode = ret;
                pINChI_Aux->nErrorCode = ret;
            }
            else
            {
                /* Success in FillOutINChI() */

#if ( bRELEASE_VERSION == 0 )
                if (pINChI->Stereo &&
                    ( pINChI->Stereo->nCompInv2Abs && !pINChI->Stereo->bTrivialInv ) ||
                     pINChI->StereoIsotopic &&
                     ( pINChI->StereoIsotopic->nCompInv2Abs && !pINChI->StereoIsotopic->bTrivialInv ))
                {
                    bExtract |= EXTR_NON_TRIVIAL_STEREO;
                }
#endif
                /*    Mark non-tautomeric representation as having
                    another, tautomeric representation */
                if (pINChI_Aux && s[TAUT_YES].nLenLinearCTTautomer)
                {
                    pINChI_Aux->bIsTautomeric = s[TAUT_YES].nLenLinearCTTautomer;
                }
#if ( bRELEASE_VERSION == 0 )
                pCS->bExtract |= bExtract;
                pINChI->bExtract |= pCS->bExtract;
#endif

                ret2 = CheckCanonNumberingCorrectness( num_atoms,
                                                      i ? num_at_tg : num_atoms,
                                                      at[i], pCS,
                                                      pCG,
                                                      i, pStrErrStruct );
                if (ret2 && pINChI_Aux) /* djb-rwth: fixing a NULL pointer dereference */
                {
                    pINChI->nErrorCode = ret2;
                    pINChI_Aux->nErrorCode = ret2;
                    ret = ret2;
                }
            }
        }

        FreeNeighList( pCS->NeighList );
        DeAllocateCS( pCS2 );

        pINChI = NULL;      /* avoid dangling pointers */
        pINChI_Aux = NULL;  /* avoid dangling pointers */
    }

    if (ret == 0)
    {
        ret = num_atoms;
    }
    /*  treat the results later */

exit_function:

    DeAllocBCN( pBCN );
    if (at[TAUT_YES])
    {
        inchi_free( at[TAUT_YES] );
    }
    if (at[TAUT_NON])
    {
        inchi_free( at[TAUT_NON] );
    }
    if (ti_out)
    {
        *ti_out = *t_group_info;
    }
    else
    {
        free_t_group_info( t_group_info );
    }
    free_t_group_info( t_group_info_orig );

    if (ebend)
    {
        inchi_free( ebend );
    }

    return ret;
}


/****************************************************************************/
#ifndef COMPILE_ANSI_ONLY /* { */
int GetAtomOrdNbrInCanonOrd( struct tagCANON_GLOBALS *pCG,
                             inp_ATOM *norm_at,
                             AT_NUMB *nAtomOrdNbr,
                             AT_NUMB *nOrigAtNosInCanonOrd,
                             int num_at )
{
    AT_NUMB   *nCanonNbr, *nOrigAtNos, *nOrigAtNosOrd;
    int          i, ret;

    ret = 0;

    nCanonNbr = (AT_NUMB *) inchi_calloc( num_at, sizeof( nCanonNbr[0] ) );
    nOrigAtNos = (AT_NUMB *) inchi_calloc( num_at, sizeof( nOrigAtNos[0] ) );
    nOrigAtNosOrd = (AT_NUMB *) inchi_calloc( num_at, sizeof( nOrigAtNosOrd[0] ) );

    if (!nCanonNbr || !nOrigAtNos || !nAtomOrdNbr || !nOrigAtNosOrd)
    {
        ret = CT_OUT_OF_RAM;  /*   <BRKPT> */
        goto exit_function;
    }
    for (i = 0; i < num_at; i++)
    {
        nCanonNbr[i] = nAtomOrdNbr[i] = nOrigAtNosOrd[i] = (AT_NUMB) i;
        nOrigAtNos[i] = norm_at[i].orig_at_number;
    }

    /*  get nCanonNbr[]: canon. numbers-1 in order of increasing original atom numbers */
    pCG->m_pn_RankForSort = nOrigAtNosInCanonOrd;
    inchi_qsort( pCG, nCanonNbr, num_at, sizeof( nCanonNbr[0] ), CompRank );
    /*  get nOrigAtNosOrd[]: norm_atom ord. numbers the same order of increasing original atom numbers */
    pCG->m_pn_RankForSort = nOrigAtNos;
    inchi_qsort( pCG, nOrigAtNosOrd, num_at, sizeof( nOrigAtNosOrd[0] ), CompRank );

    /*  check whether the 2 sets of origiginal atom numbers have identical elements */
    for (i = 0; i < num_at; i++)
    {
        if (nOrigAtNosInCanonOrd[nCanonNbr[i]] != nOrigAtNos[nOrigAtNosOrd[i]])
        {
            ret = CT_RANKING_ERR;  /*   <BRKPT> */
            goto exit_function;
        }
    }
    for (i = 0; i < num_at; i++)
    {
        nAtomOrdNbr[(int) nCanonNbr[i]] = nOrigAtNosOrd[i];
    }

    /*
        pn_RankForSort = nCanonNbr;
        qsort( nAtomOrdNbr, num_at, sizeof(nCanonNbr[0]), CompRank );
    */

exit_function:
    if (nCanonNbr)
    {
        inchi_free( nCanonNbr );
    }
    if (nOrigAtNos)
    {
        inchi_free( nOrigAtNos );
    }
    if (nOrigAtNosOrd)
    {
        inchi_free( nOrigAtNosOrd );
    }

    return ret;
}


/****************************************************************************/
int FillOutCanonInfAtom( struct tagCANON_GLOBALS *pCG,
                         inp_ATOM *norm_at,
                         INF_ATOM_DATA *inf_norm_at_data,
                         int init_num_at,
                         int bIsotopic,
                         INChI *pINChI,
                         INChI_Aux *pINChI_Aux,
                         int bAbcNumbers,
                         INCHI_MODE nMode )
{
    int          i, j, m, n, num_stereo, k, c, ret, len_str, len, atw; /* djb-rwth: removing redundant variable */
    int          next_atom[MAX_CUMULENE_LEN + 1], best_next_atom[MAX_CUMULENE_LEN + 1], cur_atom;
    int          next_neigh[MAX_CUMULENE_LEN + 1], best_next_neigh[MAX_CUMULENE_LEN + 1], best_len;
    int          num_iso_H[NUM_H_ISOTOPES];
    char        *str;
    AT_NUMB      g, e;
    int          num_at = pINChI->nNumberOfAtoms;
    int          nNumberOfTGroups = ( pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0] ) ? (int) pINChI->nTautomer[0] : 0;
    AT_NUMB     *nOrigAtNosInCanonOrd;
    INChI_Stereo *Stereo;
    AT_NUMB     *nConstitEquNumbers;
    AT_NUMB     *nConstitEquTGroupNumbers;
    S_CHAR      *t_parity = NULL;
    AT_NUMB     *nNumber = NULL;
    int          bIncludeIsotopicH;

    AT_NUMB     *nNormAtNosInCanonOrd;
    int( *MakeNumber )( char*, int, const char*, int ) = bAbcNumbers ? MakeAbcNumber : MakeDecNumber;
    int bRel = ( 0 != ( nMode & ( REQ_MODE_RELATIVE_STEREO ) ) );
    int bRac = ( 0 != ( nMode & ( REQ_MODE_RACEMIC_STEREO ) ) );
    int bRelRac = bRel || bRac;
    int bDoDisplaySp3 = 1;

    inf_ATOM *inf_norm_at = inf_norm_at_data ? inf_norm_at_data->at : NULL;

    ret = 0;
    /* djb-rwth: removing redundant code */

    if (!inf_norm_at)
    {
        return ret;
    }

    /* prepare removeable protons and H info */
    inf_norm_at_data->nNumRemovedProtons = pINChI_Aux->nNumRemovedProtons;

    MakeRemovedProtonsString( pINChI_Aux->nNumRemovedProtons, pINChI_Aux->nNumRemovedIsotopicH, NULL, bIsotopic,
                              inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H );

    /* fill out info atom */
    if (bIsotopic && !( pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups ||
        (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1) )) /* djb-rwth: addressing LLVM warning */
    {
        bIsotopic = 0;
    }

    Stereo = bIsotopic ? pINChI->StereoIsotopic :
        pINChI->Stereo;
    bDoDisplaySp3 = ( NULL != Stereo ) && ( Stereo->nNumberOfStereoCenters > 0 );

#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
    if (bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 &&
        ( Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF( Stereo->t_parity[0] ) ))
    {
        bDoDisplaySp3 = 0;
        if (Stereo->nCompInv2Abs)
        {
            inf_norm_at_data->StereoFlags |= bRel ? INF_STEREO_REL : bRac ? INF_STEREO_RAC : 0;
        }
    }
#endif
    /* flag has stereo */
    if (( NULL != Stereo ) && ( bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0 ))
    {
        inf_norm_at_data->StereoFlags |= INF_STEREO;
    }

    /*
    if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 &&
         (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) {
        bDoDisplaySp3 = 0;
    }
    */
    if (bDoDisplaySp3 && Stereo->nCompInv2Abs)
    {
        /* inversion changes stereo */
        if (bRel)
        {
            inf_norm_at_data->StereoFlags |= INF_STEREO_REL;
        }
        else
        {
            if (bRac)
            {
                inf_norm_at_data->StereoFlags |= INF_STEREO_RAC;
            }
            else
            {
                inf_norm_at_data->StereoFlags |= INF_STEREO_ABS;
            }
        }
        if (bRelRac)
        {
            inf_norm_at_data->StereoFlags |= ( Stereo->nCompInv2Abs > 0 ) ? INF_STEREO_NORM : INF_STEREO_INV;
        }
    }
    if (bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac)
    {
        /* display Inv stereo which is Absolute Stereo */
        nNumber = Stereo->nNumberInv;
        t_parity = Stereo->t_parityInv;
        nOrigAtNosInCanonOrd = bIsotopic ? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv :
            pINChI_Aux->nOrigAtNosInCanonOrdInv;
    }
    else
    {
        /* display Inv stereo which is Absolute Stereo */
        if (bDoDisplaySp3)
        {
            nNumber = Stereo->nNumber;
            t_parity = Stereo->t_parity;
        }
        nOrigAtNosInCanonOrd = bIsotopic ? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd :
            pINChI_Aux->nOrigAtNosInCanonOrd;
    }

    nConstitEquNumbers = bIsotopic ? pINChI_Aux->nConstitEquIsotopicNumbers :
        pINChI_Aux->nConstitEquNumbers;
    nConstitEquTGroupNumbers = bIsotopic ? pINChI_Aux->nConstitEquIsotopicTGroupNumbers :
        pINChI_Aux->nConstitEquTGroupNumbers;
    memset( inf_norm_at, 0, init_num_at * sizeof( inf_norm_at[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */

    /*  obtain norm_at[] atom numbers (from zero) in order of canonical numbers */
    nNormAtNosInCanonOrd = (AT_NUMB *) inchi_calloc( num_at, sizeof( nNormAtNosInCanonOrd[0] ) );
    if ((ret = GetAtomOrdNbrInCanonOrd( pCG, norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ))) /* djb-rwth: addressing LLVM warning */
    {
        goto exit_function;
    }

    /*  atom canonical and equivalence numbers > 0 */
    for (i = 0; i < num_at; i++)
    {
        j = (int) nNormAtNosInCanonOrd[i];
        if (j < 0 || j >= num_at)
        {
            continue;
        }
        inf_norm_at[j].nCanonNbr = (AT_NUMB) ( i + 1 );
        inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i];
#ifdef DISPLAY_DEBUG_DATA
        inf_norm_at[j].nDebugData = 0;
#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT )
        inf_norm_at[j].nDebugData = norm_at[j].c_point;
#endif
#endif
    }
    /*  tautomeric groups */
    if (nNumberOfTGroups)
    {
        /*
         :   start from 1: bypass number of t-groups
         :   j is a counter within the current t-group
         :   g is a tautomeric group canonical number
         :   e is a tautomeric group equivalence
         */
        for (g = 1, i = 1; g <= nNumberOfTGroups; g++)
        {
            n = (int) pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /*  number of atoms in t-group */
            e = nConstitEquTGroupNumbers[(int) g - 1];
            /*  bypass number of hydrogen atoms, negative charges, ... */
            for (i += INCHI_T_NUM_MOVABLE + 1, j = 0; j < n && i < pINChI->lenTautomer; j++, i++)
            {
                /*  scan canonical numbers of atoms within the atom t-group */
                k = (int) nNormAtNosInCanonOrd[(int) pINChI->nTautomer[i] - 1];
                inf_norm_at[k].nTautGroupCanonNbr = g;
                inf_norm_at[k].nTautGroupEquNbr = e;
            }
        }
        if (i != pINChI->lenTautomer || g != nNumberOfTGroups + 1)
        {
            ret = CT_TAUCOUNT_ERR;  /*   <BRKPT> */
            goto exit_function;
        }
    }
    /* atoms that may exchange isotopic H */
    if (bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && ( n = (int) pINChI->nPossibleLocationsOfIsotopicH[0] ))
    {
        for (i = 1; i < n; i++)
        {
            j = (int) pINChI->nPossibleLocationsOfIsotopicH[i];
            k = (int) nNormAtNosInCanonOrd[j - 1];
            if (!inf_norm_at[k].nTautGroupCanonNbr)
            {
                inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT;
            }
        }
    }

#if ( DISPLAY_RING_SYSTEMS == 1 )
    /*  debug only */
    for (j = 0; j < num_at; j++)
    {
        inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem;
        inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem;
#if ( USE_DISTANCES_FOR_RANKING == 1 )
        inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal;
        inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex;
#else
        inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex;
        inf_norm_at[j].nTautGroupEquNbr = 0;
#endif
    }
#endif

    /*  Fill inf_at[i] field 'at_string': 
        write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers
    */
    len_str = sizeof( inf_norm_at[0].at_string );
    for (i = 0; i < init_num_at; i++)
    {
        str = inf_norm_at[i].at_string;
        len = 0;
        bIncludeIsotopicH = bIsotopic && !inf_norm_at[i].nTautGroupCanonNbr && !( inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT );
        /*  isotopic mass */
        atw = 0;
        if (norm_at[i].iso_atw_diff && bIsotopic)
        {
            if (norm_at[i].at_type == ATT_PROTON)
            {
                ; /* do not set isotopic mass of a tautomeric proton */
            }
            else
            {
                if (norm_at[i].el_number == PERIODIC_NUMBER_H && norm_at[i].chem_bonds_valence == 1 &&
                      !norm_at[i].charge && !norm_at[i].radical && !norm_at[i].num_H &&
                       ( inf_norm_at[j = (int) norm_at[i].neighbor[0]].nTautGroupCanonNbr || ( inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT ) ))
                {
                    ; /* do not set isotopic mass of an exchangeable proton */
                }
                else
                {
                    atw = get_atomic_mass( norm_at[i].elname );
                    atw += ( norm_at[i].iso_atw_diff > 0 ) ? norm_at[i].iso_atw_diff - 1 : norm_at[i].iso_atw_diff;
                    /*len += sprintf( str+len, "^%d", atw );*/
                }
            }
        }
        /*  element name */
        if (norm_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3)
        {
            len += sprintf(str + len, "%s", atw == 2 ? "D" : "T");
        }
        else
        {
            if (atw)
            {
                len += sprintf(str + len, "^%d", atw);
            }
            if (strcmp( norm_at[i].elname, "Zz" )&& strcmp( norm_at[i].elname, "Zy" ))
            {
                len += sprintf(str + len, "%s", norm_at[i].elname);
            }
            else /* always show "Zy" as "Zz" */
            {
#if ( DISPLAY_ZZ_AS_STAR == 1 )
                len += sprintf(str + len, "*");
#else
                len += sprintf(str + len, "Zz");
#endif
            }
        }

        /*  hydrogens */
        /*  find number of previuosly removed terminal hydrogen atoms because these terminal H will be displayed */

        for (j = 0; j < NUM_H_ISOTOPES; j++)
        {
            num_iso_H[j] = norm_at[i].num_iso_H[j];
        }
        /* n = number of implicit H to display */
        for (j = num_at, n = (int) norm_at[i].num_H; j < init_num_at; j++)
        {
            /*  subtract number of removed terminal */
            /*  H atoms from the total number of H atoms */
            if (i == (int) norm_at[j].neighbor[0])
            {
                n -= 1; /* found explicit H => decrement number of implicit H */
                m = (int) norm_at[j].iso_atw_diff - 1;
                if (0 <= m && m < NUM_H_ISOTOPES)
                {
                    /*  subtract number of removed terminal isotopic H */
                    /*  atoms from the total number of isotopic H atoms */
                    num_iso_H[m] -= 1;
                }
            }
        }
        /* at this point n = number of implicit H to display,
           num_iso_H[] contains number of implicit isotopic H among n */
        if (bIncludeIsotopicH)
        {
            /*  subtract number of isotopic H atoms from the total number of H atoms */
            for (j = 0; j < NUM_H_ISOTOPES; j++)
            {
                n -= num_iso_H[j];
            }
        }
        /*  non-isotopic hydrogen atoms */
        if (n > 1)
        {
            len += sprintf(str + len, "H%d", n);
        }
        else
        {
            if (n == 1)
            {
                len += sprintf(str + len, "H");
            }
        }

        /*  isotopic hydrogen atoms */
        if (bIncludeIsotopicH)
        {
            for (j = 0; j < NUM_H_ISOTOPES; j++)
            {
                if (num_iso_H[j])
                {
                    if (j == 0 || (j != 1 && j != 2)) /* djb-rwth: addressing LLVM warning */
                    {
                        len += sprintf(str + len, "^%dH", j + 1);
                    }
                    else
                    {
                        len += sprintf(str + len, j == 1 ? "D" : "T");
                    }
                    if (num_iso_H[j] != 1)
                    {
                        len += sprintf(str + len, "%d", (int)num_iso_H[j]);
                    }
                }
            }
        }
        if (norm_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1])
        {
            char *q;
            if (!str[2])
            {
                str[1] = '2';  /* quick fix: replace HH with H2 */
            }
            else
            {
                if (isdigit( UCINT str[2] ) && ( n = strtol( str + 2, &q, 10 ) ) && !q[0])
                {
                    len = 1 + sprintf(str + 1, "%d", n + 1);
                }
            }
        }
        /*
        if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) {
            str[1] = '2';
        }
        */
        /*  charge */
        if (abs( norm_at[i].charge ) > 1)
        {
            len += sprintf(str + len, "%+d", norm_at[i].charge);
        }
        else
        {
            if (abs( norm_at[i].charge ) == 1)
            {
                len += sprintf(str + len, "%s", norm_at[i].charge > 0 ? "+" : "-");
            }
        }
        /*  radical */
        if (norm_at[i].radical)
        {
            len += sprintf(str + len, "%s", norm_at[i].radical == RADICAL_SINGLET ? ":" :
                norm_at[i].radical == RADICAL_DOUBLET ? "." :
                norm_at[i].radical == RADICAL_TRIPLET ? ".." : "?");
        }
    }

    /*  Stereogenic centers */
    if (bDoDisplaySp3 && Stereo && 0 < ( num_stereo = Stereo->nNumberOfStereoCenters ))
    {
        for (i = 0; i < num_stereo; i++)
        {
            j = (int) nNormAtNosInCanonOrd[(int) nNumber[i] - 1];
            c = t_parity[i];
            c = c == 1 ? '-' : c == 2 ? '+' : c == 3 ? 'u' : c == 4 ? '?' : '*';
            inf_norm_at[j].cStereoCenterParity = c;
            str = inf_norm_at[j].at_string;
            len = (int) strlen( str );
            if (len + 3 < ( int )sizeof( inf_norm_at[0].at_string ))
            {
                str[len++] = '(';
                str[len++] = inf_norm_at[j].cStereoCenterParity;
                str[len++] = ')';
                str[len] = '\0';
                /*  mark ambuguous stereo center */
                if (norm_at[j].bAmbiguousStereo && ( c == '+' || c == '-' || c == '?' ) && str[0] != '!' &&
                     len + 1 < ( int )sizeof( inf_norm_at[0].at_string ))
                {
                    memmove(str + 1, str, (long long)len + 1); /* djb-rwth: cast operator added */
                    str[0] = '!'; /* output the atom in red color */
                }
            }
        }
    }

    /*  Stereogenic bonds */
    /*  (cumulenes with odd number of double bonds are stereocenters, */
    /*   and atom parity should be set) */
    if (Stereo && 0 < ( num_stereo = Stereo->nNumberOfStereoBonds ))
    {
        for (i = 0; i < num_stereo; i++)
        {
            int start_at, bAmbiguousStereoBond = 0; /* djb-rwth: removing redundant variable */
            j = (int) nNormAtNosInCanonOrd[(int) Stereo->nBondAtom1[i] - 1];
            k = (int) nNormAtNosInCanonOrd[(int) Stereo->nBondAtom2[i] - 1];
            start_at = j;
            c = Stereo->b_parity[i];

            c = c == 1 ? '-' : c == 2 ? '+' : c == 3 ? 'u' : c == 4 ? '?' : '*';

            /*  mark ambuguous stereo bond atom(s) */
            if (norm_at[j].bAmbiguousStereo && ( c == '+' || c == '-' ) &&
                ( len = strlen( str = inf_norm_at[j].at_string ) + 1 ) < ( int )sizeof( inf_norm_at[0].at_string ) &&
                  str[0] != '!')
            {
                memmove(str + 1, str, len);
                str[0] = '!'; /* output the atom in red color */
                bAmbiguousStereoBond++;
            }

            if (norm_at[k].bAmbiguousStereo && ( c == '+' || c == '-' ) &&
                ( len = strlen( str = inf_norm_at[k].at_string ) + 1 ) < ( int )sizeof( inf_norm_at[0].at_string ) &&
                  str[0] != '!')
            {
                memmove(str + 1, str, len);
                str[0] = '!'; /* output the atom in red color */
                bAmbiguousStereoBond++;
            }

            /*  find the opposite atom k. */
            /*  Note: since it may be a cumulene, find the shortest(best) path */
            /*  to atom number k to avoid confusion in case of, for example, */
            /*  4-member aromatic rings. */
            best_len = MAX_CUMULENE_LEN + 1; /* moved here from inside the cycle 1-8-2003 */
            for (n = 0; n < norm_at[j].valence; n++)
            {
                if (norm_at[j].bond_type[n] == BOND_SINGLE)
                {
                    /*  single bond cannot be stereogenic. */
                    continue;
                }
                /* best_len = MAX_CUMULENE_LEN+1; */
                len = 0; /*  number of bonds in cumulene - 1 */
                next_atom[len] = (int) norm_at[j].neighbor[n];
                next_neigh[len] = n;
                cur_atom = j;
                while (next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence)
                {
                    next_neigh[len + 1] = ( (int) norm_at[next_atom[len]].neighbor[0] == cur_atom );
                    next_atom[len + 1] = (int) norm_at[next_atom[len]].neighbor[next_neigh[len + 1]];
                    cur_atom = next_atom[len];
                    len++;
                }
                if (next_atom[len] == k)
                {
                    if (len < best_len)
                    {
                        memcpy(best_next_neigh, next_neigh, sizeof(best_next_neigh));
                        memcpy(best_next_atom, next_atom, sizeof(best_next_atom));
                        best_len = len;
                        /* djb-rwth: removing redundant code */
                        if (len == 0)
                        {
                            break; /*  path length cannot be smaller than 1 */
                        }
                    }
                    /* djb-rwth: removing redundant code */
                }
            }
            if (best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k)
            {
                /* djb-rwth: removing redundant code */
                if (best_len % 2)
                {
                    /*  even number of bonds: chiral atom, draw parity on the cenrtal atom */
                    j = best_next_atom[best_len / 2];
                    inf_norm_at[j].cStereoCenterParity = c;
                    str = inf_norm_at[j].at_string;
                    len = (int) strlen( str );
                    if (len + 3 < ( int )sizeof( inf_norm_at[0].at_string ))
                    {
                        str[len++] = '(';
                        str[len++] = inf_norm_at[j].cStereoCenterParity;
                        str[len++] = ')';
                        str[len] = '\0';
                    }
                }
                else
                {
                    /*  odd number of bonds: draw parity on the central bond */
                    if (best_len == 0)
                    {
                        /*  double bond */
                        j = start_at;
                        k = best_next_neigh[0];
                    }
                    else
                    {
                        /*  cumulene */
                        best_len = best_len / 2 - 1;
                        j = best_next_atom[best_len];
                        k = best_next_neigh[best_len + 1]; /*  added +1 to display cumulene parity on the middle bond (6-24-2002) */
                    }
                    /*  mark "forward" bond */
                    for (m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m++)
                        ;
                    if (m < MAX_STEREO_BONDS)
                    {
                        inf_norm_at[j].cStereoBondParity[m] = c;
                        inf_norm_at[j].cStereoBondNumber[m] = k;
                        inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond;
                    }
                    /* djb-rwth: removing redundant code */
                    /*  mark "backward" bond */
                    n = norm_at[j].neighbor[k];
                    for (k = 0; k < norm_at[n].valence && j != (int) norm_at[n].neighbor[k]; k++)
                    {
                        ;
                    }
                    if (k < norm_at[n].valence)
                    {
                        j = n;
                        for (m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m++)
                        {
                            ;
                        }
                        if (m < MAX_STEREO_BONDS)
                        {
                            inf_norm_at[j].cStereoBondParity[m] = c;
                            inf_norm_at[j].cStereoBondNumber[m] = k;
                            inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond;
                        }
                        /* djb-rwth: removing redundant code */
                    }
                    /* djb-rwth: removing redundant code */
                }
            }
            /* djb-rwth: removing redundant code */
        }
    }

    for (i = 0; i < init_num_at; i++)
    {
        /*  canonical numbers */
        if (inf_norm_at[i].nCanonNbr)
        {
            str = inf_norm_at[i].at_string;
            len = (int) strlen( str );
            len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nCanonNbr );
            if (inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || ( inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT ))
            {
                if (inf_norm_at[i].nCanonEquNbr)
                {
                    len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nCanonEquNbr );
                }
                else
                {
                    if (len + 1 < len_str)
                    {
                        len += 1;
                        strcat(str, "/");
                    }
                }
            }
            /*  tautomeric groups */
            if (inf_norm_at[i].nTautGroupCanonNbr)
            {
                len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nTautGroupCanonNbr );
                if (inf_norm_at[i].nTautGroupEquNbr)
                {
                    len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nTautGroupEquNbr );
                }
            }
            if (( inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT ) && len + 2 <= len_str)
            {
                str[len++] = '/';
                str[len++] = '*';
                str[len] = '\0';
            }
#ifdef DISPLAY_DEBUG_DATA
            if (inf_norm_at[i].nDebugData)
            {
                len += ( *MakeNumber )( str + len, len_str - len, "`", (int) inf_norm_at[i].nDebugData );
            }
#endif
        }
    }

exit_function:

    if (nNormAtNosInCanonOrd)
    {
        inchi_free( nNormAtNosInCanonOrd );
    }

    return ret;
}


/****************************************************************************/
int FillOutOneCanonInfAtom( struct tagCANON_GLOBALS *pCG,
                            inp_ATOM *inp_norm_at,
                            INF_ATOM_DATA *inf_norm_at_data,
                            AT_NUMB *pStereoFlags,
                            int init_num_at,
                            int offset,
                            int offset_H,
                            int bIsotopic,
                            INChI *pINChI,
                            INChI_Aux *pINChI_Aux,
                            int bAbcNumbers,
                            INCHI_MODE nMode )
{
    int          i, j, m, n, num_stereo, k, c, ret, len_str, len, atw; /* djb-rwth: removing redundant variable */
    int          next_atom[MAX_CUMULENE_LEN + 1], best_next_atom[MAX_CUMULENE_LEN + 1], cur_atom;
    int          next_neigh[MAX_CUMULENE_LEN + 1], best_next_neigh[MAX_CUMULENE_LEN + 1], best_len, bIncludeIsotopicH;
    int          num_iso_H[NUM_H_ISOTOPES];
    char        *str;
    AT_NUMB      g, e;
    int          num_at = pINChI->nNumberOfAtoms;
    int          num_H = init_num_at - num_at; /* number of removed H */
    int          nNumberOfTGroups = ( pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0] ) ? (int) pINChI->nTautomer[0] : 0;
    AT_NUMB     *nOrigAtNosInCanonOrd;
    INChI_Stereo *Stereo;
    AT_NUMB     *nConstitEquNumbers;
    AT_NUMB     *nConstitEquTGroupNumbers;
    S_CHAR      *t_parity = NULL;
    AT_NUMB     *nNumber = NULL;

    AT_NUMB     *nNormAtNosInCanonOrd;
    int( *MakeNumber )( char*, int, const char*, int ) = bAbcNumbers ? MakeAbcNumber : MakeDecNumber;
    int bRel = ( 0 != ( nMode & ( REQ_MODE_RELATIVE_STEREO ) ) );
    int bRac = ( 0 != ( nMode & ( REQ_MODE_RACEMIC_STEREO ) ) );
    int bRelRac = bRel || bRac;
    int bDoDisplaySp3 = 1;

    inf_ATOM *inf_norm_at = ( inf_norm_at_data && inf_norm_at_data->at ) ? inf_norm_at_data->at + offset : NULL;
    inp_ATOM *norm_at = inp_norm_at ? inp_norm_at + offset : NULL;
    inf_ATOM *inf_norm_at_H = ( inf_norm_at_data && inf_norm_at_data->at ) ? inf_norm_at_data->at + offset_H : NULL;
    inp_ATOM *norm_at_H = inp_norm_at ? inp_norm_at + offset_H : NULL;

    ret = 0;
    /* djb-rwth: removing redundant code */

    if (!inf_norm_at)
        return ret;
    /* -- already added in FillOutCompositeCanonInfAtom() --
    if ( bIsotopic ) {
        for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) {
            if ( pINChI_Aux->nNumRemovedIsotopicH[i] ) {
                inf_norm_at_data->num_iso_H[i] += pINChI_Aux->nNumRemovedIsotopicH[i];
                inf_norm_at_data->num_removed_iso_H ++;
            }
        }
    }
    */

    if (bIsotopic && !( pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups ||
        (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1) )) /* djb-rwth: addressing LLVM warning */
    {
        bIsotopic = 0;
    }

    Stereo = bIsotopic ? pINChI->StereoIsotopic : pINChI->Stereo;

    bDoDisplaySp3 = ( NULL != Stereo ) && ( Stereo->nNumberOfStereoCenters > 0 );

#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
    if (bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 &&
        ( Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF( Stereo->t_parity[0] ) ))
    {
        bDoDisplaySp3 = 0;
        if (Stereo->nCompInv2Abs)
        {
            inf_norm_at_data->StereoFlags |= bRel ? INF_STEREO_REL : bRac ? INF_STEREO_RAC : 0;
        }
    }
#endif
    /* flag has stereo */
    if (( NULL != Stereo ) && ( bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0 ))
    {
        ( *pStereoFlags ) |= INF_STEREO;
    }

    /*
    if ( bDoDisplaySp3 && bRelRac && Stereo->nCompInv2Abs && Stereo->nNumberOfStereoCenters < 2 ) {
        bDoDisplaySp3 = 0;
    }
    */
    if (bDoDisplaySp3 && Stereo->nCompInv2Abs)
    {
        /* inversion changes stereo */
        if (bRel)
        {
            ( *pStereoFlags ) |= INF_STEREO_REL;
        }
        else
        {
            if (bRac)
            {
                ( *pStereoFlags ) |= INF_STEREO_RAC;
            }
            else
            {
                ( *pStereoFlags ) |= INF_STEREO_ABS;
            }
        }
        if (bRelRac)
        {
            ( *pStereoFlags ) |= ( Stereo->nCompInv2Abs > 0 ) ? INF_STEREO_NORM : INF_STEREO_INV;
        }
    }
    if (bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac)
    {
        /* display Inv stereo which is Absolute Stereo */
        nNumber = Stereo->nNumberInv;
        t_parity = Stereo->t_parityInv;
        nOrigAtNosInCanonOrd = bIsotopic ? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv :
            pINChI_Aux->nOrigAtNosInCanonOrdInv;
    }
    else
    {
         /* display Output stereo which is Absolute Stereo */
        if (bDoDisplaySp3)
        {
            nNumber = Stereo->nNumber;
            t_parity = Stereo->t_parity;
        }
        nOrigAtNosInCanonOrd = bIsotopic
            ? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd
            : pINChI_Aux->nOrigAtNosInCanonOrd;
    }

    nConstitEquNumbers = bIsotopic ? pINChI_Aux->nConstitEquIsotopicNumbers :
        pINChI_Aux->nConstitEquNumbers;
    nConstitEquTGroupNumbers = bIsotopic ? pINChI_Aux->nConstitEquIsotopicTGroupNumbers :
        pINChI_Aux->nConstitEquTGroupNumbers;
    memset( inf_norm_at, 0, num_at * sizeof( inf_norm_at[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    if (num_H > 0)
    {
        memset( inf_norm_at_H, 0, num_H * sizeof( inf_norm_at[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    }

    /*  obtain norm_at[] atom numbers (from zero) in order of canonical numbers */
    nNormAtNosInCanonOrd = (AT_NUMB *) inchi_calloc( num_at, sizeof( nNormAtNosInCanonOrd[0] ) );
    if ((ret = GetAtomOrdNbrInCanonOrd( pCG, norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ))) /* djb-rwth: addressing LLVM warning */
    {
        goto exit_function;
    }

    /*  atom canonical and equivalence numbers > 0 */
    for (i = 0; i < num_at; i++)
    {
        j = (int) nNormAtNosInCanonOrd[i];
        if (j < 0 || j >= num_at)
        {
            continue;
        }
        inf_norm_at[j].nCanonNbr = (AT_NUMB) ( i + 1 );
        inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i];
#ifdef DISPLAY_DEBUG_DATA
        inf_norm_at[j].nDebugData = 0;
#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT )
        inf_norm_at[j].nDebugData = norm_at[j].c_point;
#endif
#endif
    }
    /*  tautomeric groups */
    if (nNumberOfTGroups)
    {
        /*
         :   start from 1: bypass number of t-groups
         :   j is a counter within the current t-group
         :   g is a tautomeric group canonical number
         :   e is a tautomeric group equivalence
         */
        for (g = 1, i = 1; g <= nNumberOfTGroups; g++)
        {
            n = (int) pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /*  number of atoms in t-group */
            e = nConstitEquTGroupNumbers[(int) g - 1];
            /*  bypass number of hydrogen atoms, negative charges, ... */
            for (i += INCHI_T_NUM_MOVABLE + 1, j = 0; j < n && i < pINChI->lenTautomer; j++, i++)
            {
                /*  scan canonical numbers of atoms within the atom t-group */
                k = (int) nNormAtNosInCanonOrd[(int) pINChI->nTautomer[i] - 1];
                inf_norm_at[k].nTautGroupCanonNbr = g;
                inf_norm_at[k].nTautGroupEquNbr = e;
            }
        }
        if (i != pINChI->lenTautomer || g != nNumberOfTGroups + 1)
        {
            ret = CT_TAUCOUNT_ERR;  /*   <BRKPT> */
            goto exit_function;
        }
    }
    /* atoms that may exchange isotopic H */
    if (bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && ( n = (int) pINChI->nPossibleLocationsOfIsotopicH[0] ))
    {
        for (i = 1; i < n; i++)
        {
            j = (int) pINChI->nPossibleLocationsOfIsotopicH[i];
            k = (int) nNormAtNosInCanonOrd[j - 1];
            if (!inf_norm_at[k].nTautGroupCanonNbr)
            {
                inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT;
            }
        }
    }
#if ( DISPLAY_RING_SYSTEMS == 1 )
    /*  debug only */
    for (j = 0; j < num_at; j++)
    {
        inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem;
        inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem;
#if ( USE_DISTANCES_FOR_RANKING == 1 )
        inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal;
        inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex;
#else
        inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex;
        inf_norm_at[j].nTautGroupEquNbr = 0;
#endif
    }
#endif

    /*	Fill inf_at[i] field 'at_string': 
        write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon.numbers 
    */
    len_str = sizeof( inf_norm_at[0].at_string );
    for (i = 0; i < init_num_at; i++)
    {
        inf_ATOM *cur_inf_norm_at = ( i < num_at ) ? inf_norm_at + i : inf_norm_at_H + i - num_at;
        inp_ATOM *cur_norm_at = ( i < num_at ) ? norm_at + i : norm_at_H + i - num_at;
        str = cur_inf_norm_at->at_string;
        len = 0;
        bIncludeIsotopicH = bIsotopic && ( i >= num_at || (!inf_norm_at[i].nTautGroupCanonNbr && !( inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT )) ); /* djb-rwth: addressing LLVM warning */
        /*  isotopic mass */
        atw = 0;
        if (cur_norm_at) /* djb-rwth: fixing a NULL pointer dereference */
        {
            if (cur_norm_at->iso_atw_diff && bIsotopic)
            {
                if (cur_norm_at->at_type == ATT_PROTON)
                {
                    ; /* do not set isotopic mass of a tautomeric proton */
                }
                else
                {
                    if (num_at <= i && cur_norm_at->el_number == PERIODIC_NUMBER_H && cur_norm_at->chem_bonds_valence == 1 &&
                        !cur_norm_at->charge && !cur_norm_at->radical && !cur_norm_at->num_H &&
                        0 <= (j = (int)cur_norm_at->neighbor[0] - offset) && j < num_at &&
                        (inf_norm_at[j].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT)))
                    {
                        ; /* do not set isotopic mass of an exchangeable proton */
                    }
                    else
                    {
                        atw = get_atomic_mass(cur_norm_at->elname);
                        atw += (cur_norm_at->iso_atw_diff > 0) ? cur_norm_at->iso_atw_diff - 1 : cur_norm_at->iso_atw_diff;
                        /*len += sprintf( str+len, "^%d", atw );*/
                    }
                }
            }
            /*  element name */
            if (cur_norm_at->el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3)
            {
                len += sprintf(str + len, "%s", atw == 2 ? "D" : "T");
            }
            else
            {
                if (atw)
                {
                    len += sprintf(str + len, "^%d", atw);
                }
                /*if (strcmp( cur_norm_at->elname, "Zz" ))*/
                if (strcmp(cur_norm_at->elname, "Zz") && strcmp(cur_norm_at->elname, "Zy"))
                {
                    len += sprintf(str + len, "%s", cur_norm_at->elname);
                }
                else /* always show "Zy" as "Zz" */
                {
#if ( DISPLAY_ZZ_AS_STAR == 1 )
                    len += sprintf(str + len, "*");
#else
                    len += sprintf(str + len, "Zz");
#endif
                }
            }

            /*  hydrogens */
            /*  find number of previuosly removed terminal hydrogen atoms */
            for (j = 0; j < NUM_H_ISOTOPES; j++)
            {
                num_iso_H[j] = cur_norm_at->num_iso_H[j];
            }
            for (j = 0, n = (int)cur_norm_at->num_H; j < num_H; j++)
            {
                /*  subtract number of removed terminal */
                /*  H atoms from the total number of H atoms */
                if (i == (int)norm_at_H[j].neighbor[0] - offset)
                {
                    n -= 1;
                    m = (int)norm_at_H[j].iso_atw_diff - 1;
                    if (0 <= m && m < NUM_H_ISOTOPES)
                    {
                        /*  subtract number of removed terminal isotopic */
                        /*  H atoms from the total number of isotopic H atoms */
                        num_iso_H[m] -= 1;
                    }
                }
            }
            if (bIncludeIsotopicH)
            {
                /*  subtract number of isotopic H atoms from the total number of H atoms */
                for (j = 0; j < NUM_H_ISOTOPES; j++)
                {
                    n -= num_iso_H[j];
                }
            }
            /*  non-isotopic hydrogen atoms */
            if (n > 1)
            {
                len += sprintf(str + len, "H%d", n);
            }
            else
            {
                if (n == 1)
                {
                    len += sprintf(str + len, "H");
                }
            }

            /*  isotopic hydrogen atoms */
            if (bIncludeIsotopicH)
            {
                for (j = 0; j < NUM_H_ISOTOPES; j++)
                {
                    if (num_iso_H[j])
                    {
                        if (j == 0 || (j != 1 && j != 2)) /* djb-rwth: addressing LLVM warning */
                        {
                            len += sprintf(str + len, "^%dH", j + 1);
                        }
                        else
                        {
                            len += sprintf(str + len, j == 1 ? "D" : "T");
                        }
                        if (num_iso_H[j] != 1)
                        {
                            len += sprintf(str + len, "%d", (int)num_iso_H[j]);
                        }
                    }
                }
            }
            if (cur_norm_at->el_number == PERIODIC_NUMBER_H && str[0] == str[1])
            {
                char* q;
                if (!str[2])
                {
                    str[1] = '2';  /* quick fix: replace HH with H2 */
                }
                else
                {
                    if (isdigit(UCINT str[2]) && (n = strtol(str + 2, &q, 10)) && !q[0])
                    {
                        len = 1 + sprintf(str + 1, "%d", n + 1);
                    }
                }
            }
            /*  charge */
            if (abs(cur_norm_at->charge) > 1)
            {
                len += sprintf(str + len, "%+d", cur_norm_at->charge);
            }
            else
            {
                if (abs(cur_norm_at->charge) == 1)
                {
                    len += sprintf(str + len, "%s", cur_norm_at->charge > 0 ? "+" : "-");
                }
            }
            /*  radical */
            if (cur_norm_at->radical)
            {
                len += sprintf(str + len, "%s", cur_norm_at->radical == RADICAL_SINGLET ? ":" :
                    cur_norm_at->radical == RADICAL_DOUBLET ? "." :
                    cur_norm_at->radical == RADICAL_TRIPLET ? ".." : "?");
            }
        }
    }

    /*  Stereogenic centers */
    if (bDoDisplaySp3 && Stereo && 0 < ( num_stereo = Stereo->nNumberOfStereoCenters ))
    {
        for (i = 0; i < num_stereo; i++)
        {
            j = (int) nNormAtNosInCanonOrd[(int) nNumber[i] - 1];
            c = t_parity[i];
            c = c == 1 ? '-' : c == 2 ? '+' : c == 3 ? 'u' : c == 4 ? '?' : '*';
            inf_norm_at[j].cStereoCenterParity = c;
            str = inf_norm_at[j].at_string;
            len = (int) strlen( str );
            if (len + 3 < ( int )sizeof( inf_norm_at[0].at_string ))
            {
                str[len++] = '(';
                str[len++] = inf_norm_at[j].cStereoCenterParity;
                str[len++] = ')';
                str[len] = '\0';
                /*  mark ambuguous stereo center */
                if (norm_at[j].bAmbiguousStereo && ( c == '+' || c == '-' || c == '?' ) && str[0] != '!' &&
                     len + 1 < ( int )sizeof( inf_norm_at[0].at_string ))
                {
                    memmove(str + 1, str, (long long)len + 1); /* djb-rwth: cast operator added */
                    str[0] = '!'; /* output the atom in red color */
                }
            }
        }
    }

    /*  Stereogenic bonds */
    /*  (cumulenes with odd number of double bonds are stereocenters, */
    /*   and atom parity should be set) */
    if (Stereo && 0 < ( num_stereo = Stereo->nNumberOfStereoBonds ))
    {
        for (i = 0; i < num_stereo; i++)
        {
            int start_at, bAmbiguousStereoBond = 0; /* djb-rwth: removing redundant variable */
            j = (int) nNormAtNosInCanonOrd[(int) Stereo->nBondAtom1[i] - 1];
            k = (int) nNormAtNosInCanonOrd[(int) Stereo->nBondAtom2[i] - 1];
            start_at = j;
            c = Stereo->b_parity[i];

            c = c == 1 ? '-' : c == 2 ? '+' : c == 3 ? 'u' : c == 4 ? '?' : '*';

            /*  mark ambuguous stereo bond atom(s) */
            if (norm_at[j].bAmbiguousStereo && ( c == '+' || c == '-' ) &&
                ( len = strlen( str = inf_norm_at[j].at_string ) + 1 ) < ( int )sizeof( inf_norm_at[0].at_string ) &&
                  str[0] != '!')
            {
                memmove(str + 1, str, len);
                str[0] = '!'; /* output the atom in red color */
                bAmbiguousStereoBond++;
            }
            if (norm_at[k].bAmbiguousStereo && ( c == '+' || c == '-' ) &&
                ( len = strlen( str = inf_norm_at[k].at_string ) + 1 ) < ( int )sizeof( inf_norm_at[0].at_string ) &&
                  str[0] != '!')
            {
                memmove(str + 1, str, len);
                str[0] = '!'; /* output the atom in red color */
                bAmbiguousStereoBond++;
            }

            /*  find the opposite atom k. */
            /*  Note: since it may be a cumulene, find the shortest(best) path */
            /*  to atom number k to avoid confusion in case of, for example, */
            /*  4-member aromatic rings. */
            best_len = MAX_CUMULENE_LEN + 1; /* moved here from inside the cycle 1-8-2003 */
            for (n = 0; n < norm_at[j].valence; n++)
            {
                if (norm_at[j].bond_type[n] == BOND_SINGLE)
                {
                    /*  single bond cannot be stereogenic. */
                    continue;
                }
                /* best_len = MAX_CUMULENE_LEN+1; */
                len = 0; /*  number of bonds in cumulene - 1 */
                next_atom[len] = (int) norm_at[j].neighbor[n] - offset;
                next_neigh[len] = n;
                cur_atom = j;
                while (next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence)
                {
                    next_neigh[len + 1] = ( (int) norm_at[next_atom[len]].neighbor[0] - offset == cur_atom );
                    next_atom[len + 1] = (int) norm_at[next_atom[len]].neighbor[next_neigh[len + 1]] - offset;
                    cur_atom = next_atom[len];
                    len++;
                }
                if (next_atom[len] == k)
                {
                    if (len < best_len)
                    {
                        memcpy(best_next_neigh, next_neigh, sizeof(best_next_neigh));
                        memcpy(best_next_atom, next_atom, sizeof(best_next_atom));
                        best_len = len;
                        /* djb-rwth: removing redundant code */
                        if (len == 0)
                        {
                            break; /*  path length cannot be smaller than 1 */
                        }
                    }
                    /* djb-rwth: removing redundant code */
                }
            }
            if (best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k)
            {
                /* djb-rwth: removing redundant code */
                if (best_len % 2)
                {
                    /*  even number of bonds: chiral atom, draw parity on the cenrtal atom */
                    j = best_next_atom[best_len / 2];
                    inf_norm_at[j].cStereoCenterParity = c;
                    str = inf_norm_at[j].at_string;
                    len = (int) strlen( str );
                    if (len + 3 < ( int )sizeof( inf_norm_at[0].at_string ))
                    {
                        str[len++] = '(';
                        str[len++] = inf_norm_at[j].cStereoCenterParity;
                        str[len++] = ')';
                        str[len] = '\0';
                    }
                }
                else
                {
                    /*  odd number of bonds: draw parity on the central bond */
                    if (best_len == 0)
                    {
                        /*  double bond */
                        j = start_at;
                        k = best_next_neigh[0];
                    }
                    else
                    {
                        /*  cumulene */
                        best_len = best_len / 2 - 1;
                        j = best_next_atom[best_len];
                        k = best_next_neigh[best_len + 1]; /*  added +1 to display cumulene parity on the middle bond (6-24-2002) */
                    }
                    /*  mark "forward" bond */
                    for (m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m++)
                        ;
                    if (m < MAX_STEREO_BONDS)
                    {
                        inf_norm_at[j].cStereoBondParity[m] = c;
                        inf_norm_at[j].cStereoBondNumber[m] = k;
                        inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond;
                    }
                    /* djb-rwth: removing redundant code */
                    /*  mark "backward" bond */
                    n = norm_at[j].neighbor[k] - offset;
                    for (k = 0; k < norm_at[n].valence && j != (int) norm_at[n].neighbor[k] - offset; k++)
                    {
                        ;
                    }
                    if (k < norm_at[n].valence)
                    {
                        j = n;
                        for (m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m++)
                        {
                            ;
                        }
                        if (m < MAX_STEREO_BONDS)
                        {
                            inf_norm_at[j].cStereoBondParity[m] = c;
                            inf_norm_at[j].cStereoBondNumber[m] = k;
                            inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond;
                        }
                        /* djb-rwth: removing redundant code */
                    }
                    /* djb-rwth: removing redundant code */
                }
            }
            /* djb-rwth: removing redundant code */
        }
    }

    for (i = 0; i < num_at; i++)
    {
        /*  canonical numbers */
        if (inf_norm_at[i].nCanonNbr)
        {
            str = inf_norm_at[i].at_string;
            len = (int) strlen( str );
#define SHOW_GLOBAL_CANO_NUMBERING 1
#if SHOW_GLOBAL_CANO_NUMBERING==1
            len += (*MakeNumber)(str + len, len_str - len, "/", (int)inf_norm_at[i].nCanonNbr); // -offset );
#else
            len += (*MakeNumber)(str + len, len_str - len, "/", (int)inf_norm_at[i].nCanonNbr);
#endif
            if (inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || ( inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT ))
            {
                if (inf_norm_at[i].nCanonEquNbr)
                {
                    len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nCanonEquNbr );
                }
                else
                {
                    if (len + 1 < len_str)
                    {
                        len += 1;
                        strcat(str, "/");
                    }
                }
            }
            /*  tautomeric groups */
            if (inf_norm_at[i].nTautGroupCanonNbr)
            {
                len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nTautGroupCanonNbr );
                if (inf_norm_at[i].nTautGroupEquNbr)
                {
                    len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_norm_at[i].nTautGroupEquNbr );
                }
            }
            if (( inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT ) && len + 2 <= len_str)
            {
                str[len++] = '/';
                str[len++] = '*';
                str[len] = '\0';
            }
#ifdef DISPLAY_DEBUG_DATA
            if (inf_norm_at[i].nDebugData)
            {
                len += ( *MakeNumber )( str + len, len_str - len, "`", (int) inf_norm_at[i].nDebugData );
            }
#endif
        }
    }

exit_function:

    if (nNormAtNosInCanonOrd)
    {
        inchi_free( nNormAtNosInCanonOrd );
    }

    return ret;
}


/****************************************************************************/
int FillOutInputInfAtom( inp_ATOM *inp_at,
                         INF_ATOM_DATA *inf_at_data,
                         int init_num_at,
                         int num_removed_H,
                         int bAdd_DT_to_num_H,
                         int nNumRemovedProtons,
                         NUM_H *nNumRemovedProtonsIsotopic,
                         int bIsotopic,
                         int bAbcNumbers ) /* djb-rwth: this whole function seems to be useless as it returns the value of a function argument -- init_num_at */
{
    int          i, j, m, n, ret, len_str, len, atw;
    int          num_iso_H[NUM_H_ISOTOPES];
    char        *str;
    int          num_at = init_num_at - num_removed_H;
    int( *MakeNumber )( char*, int, const char*, int ) = MakeDecNumber;

    inf_ATOM *inf_at = inf_at_data ? inf_at_data->at : NULL;

    ret = 0;

    if (!inf_at)
    {
        return ret;
    }

    memset( inf_at, 0, init_num_at * sizeof( inf_at[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */

    inf_at_data->nNumRemovedProtons = nNumRemovedProtons;
    MakeRemovedProtonsString( nNumRemovedProtons, nNumRemovedProtonsIsotopic, NULL, bIsotopic, inf_at_data->szRemovedProtons, NULL );
    /*  atom canonical and equivalence numbers > 0 */
    for (i = 0; i < num_at; i++)
    {
#if ( DISPLAY_ORIG_AT_NUMBERS == 1 )
        inf_at[i].nCanonNbr = inp_at[i].orig_at_number;
#else
        inf_at[i].nCanonNbr = (AT_NUMB) ( i + 1 );
#endif
    }
    /*  Fill inf_at[i] field 'at_string': 
        write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers 
    */
    len_str = sizeof( inf_at[0].at_string );
    for (i = 0; i < init_num_at; i++)
    {
        str = inf_at[i].at_string;
        len = 0;
        /*  isotopic mass */
        atw = 0;
        if (inp_at[i].iso_atw_diff && bIsotopic)
        {
            atw = get_atomic_mass( inp_at[i].elname );
            atw += ( inp_at[i].iso_atw_diff > 0 ) ? inp_at[i].iso_atw_diff - 1 : inp_at[i].iso_atw_diff;
            /*len += sprintf( str+len, "^%d", atw );*/
        }
        /*  element name */
        if (inp_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3)
        {
            len += sprintf(str + len, "%s", atw == 2 ? "D" : "T");
        }
        else
        {
            if (atw)
            {
                len += sprintf(str + len, "^%d", atw);
            }
            /*if (strcmp( inp_at[i].elname, "Zz" ))*/
            if (strcmp( inp_at[i].elname, "Zz" ) && strcmp( inp_at[i].elname, "Zy" ))
            {
                len += sprintf(str + len, "%s", inp_at[i].elname);
            }
            else /* always show "Zy" as "Zz" */
            {
#if ( DISPLAY_ZZ_AS_STAR == 1 )
                len += sprintf(str + len, "*");
#else
                len += sprintf(str + len, "Zz");
#endif
            }
        }

        /*  hydrogens */
        /*  find number of previuosly removed terminal hydrogen atoms */
        for (j = 0; j < NUM_H_ISOTOPES; j++)
        {
            num_iso_H[j] = inp_at[i].num_iso_H[j];
        }
        for (j = num_at, n = (int) inp_at[i].num_H; j < init_num_at; j++)
        {
            /*  subtract number of removed terminal */
            /*  H atoms from the total number of H atoms */
            if (i == (int) inp_at[j].neighbor[0])
            {
                n -= 1;
                m = (int) inp_at[j].iso_atw_diff - 1;
                if (0 <= m && m < NUM_H_ISOTOPES)
                {
                    /*  subtract number of removed terminal isotopic */
                    /*  H atoms from the total number of isotopic H atoms */
                    num_iso_H[m] -= 1;
                }
            }
        }
        if (bIsotopic && !bAdd_DT_to_num_H)
        {
            /*  subtract number of isotopic H atoms from the total number of H atoms */
            for (j = 0; j < NUM_H_ISOTOPES; j++)
            {
                n -= num_iso_H[j];
            }
        }
        /*  non-isotopic hydrogen atoms */
        if (n > 1)
        {
            len += sprintf(str + len, "H%d", n);
        }
        else
        {
            if (n == 1)
            {
                len += sprintf(str + len, "H"); /* fixed 12-21-2002: removed 3rd argument */
            }
        }
        if (bIsotopic)
        {
            /*  isotopic hydrogen atoms */
            for (j = 0; j < NUM_H_ISOTOPES; j++)
            {
                if (num_iso_H[j])
                {
                    if (j == 0 || (j != 1 && j != 2))
                    {
                        len += sprintf(str + len, "^%dH", j + 1);
                    }
                    else
                    {
                        len += sprintf(str + len, j == 1 ? "D" : "T");
                    }
                    if (num_iso_H[j] != 1)
                    {
                        len += sprintf(str + len, "%d", (int)num_iso_H[j]);
                    }
                }
            }
        }
        if (inp_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1])
        {
            char *q;
            if (!str[2])
            {
                str[1] = '2';  /* quick fix: replace HH with H2 */
            }
            else
            {
                if (isdigit( UCINT str[2] ) && ( n = strtol( str + 2, &q, 10 ) ) && !q[0])
                {
                    len = 1 + sprintf(str + 1, "%d", n + 1);
                }
            }
        }
        /*
        if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) {
            str[1] = '2';
        }
        */
        /*  charge */
        if (abs( inp_at[i].charge ) > 1)
        {
            len += sprintf(str + len, "%+d", inp_at[i].charge);
        }
        else
        {
            if (abs( inp_at[i].charge ) == 1)
            {
                len += sprintf(str + len, "%s", inp_at[i].charge > 0 ? "+" : "-");
            }
        }

        /*  radical */
        if (inp_at[i].radical)
        {
            len += sprintf(str + len, "%s", inp_at[i].radical == RADICAL_SINGLET ? ":" :
                inp_at[i].radical == RADICAL_DOUBLET ? "." :
                inp_at[i].radical == RADICAL_TRIPLET ? ".." : "?");
        }
    }

    for (i = 0; i < init_num_at; i++)
    {
        /*  canonical numbers */
        if (inf_at[i].nCanonNbr)
        {
            str = inf_at[i].at_string;
            len = (int) strlen( str );
            len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_at[i].nCanonNbr );
            if (inf_at[i].nCanonEquNbr || inf_at[i].nTautGroupCanonNbr)
            {
                len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_at[i].nCanonEquNbr );
            }
            /*  tautomeric groups */
            if (inf_at[i].nTautGroupCanonNbr)
            {
                len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_at[i].nTautGroupCanonNbr );
                if (inf_at[i].nTautGroupEquNbr)
                {
                    len += ( *MakeNumber )( str + len, len_str - len, "/", (int) inf_at[i].nTautGroupEquNbr ); /* djb-rwth: ignoring LLVM warning: variable used? */
                }
            }
        }
    }
    ret = init_num_at;

    return ret;
}


/****************************************************************************/
int FillOutInfAtom( struct tagCANON_GLOBALS *pCG,
                    inp_ATOM *norm_at,
                    INF_ATOM_DATA *inf_norm_at_data,
                    int init_num_at,
                    int num_removed_H,
                    int bAdd_DT_to_num_H,
                    int nNumRemovedProtons,
                    NUM_H *nNumRemovedProtonsIsotopic,
                    int bIsotopic,
                    INChI *pINChI,
                    INChI_Aux *pINChI_Aux,
                    int bAbcNumbers,
                    INCHI_MODE nMode )
{
    if (norm_at && inf_norm_at_data && inf_norm_at_data->at)
    {
        if (pINChI && pINChI_Aux)
        {
            return FillOutCanonInfAtom( pCG, norm_at, inf_norm_at_data, init_num_at, bIsotopic, pINChI,
                                        pINChI_Aux, bAbcNumbers, nMode );
        }
        else
        {
            return FillOutInputInfAtom( norm_at, inf_norm_at_data, init_num_at, num_removed_H, bAdd_DT_to_num_H,
                                        nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, bAbcNumbers );
        }
    }

    return 0;
}


/****************************************************************************/
int FillOutCompositeCanonInfAtom( struct tagCANON_GLOBALS *pCG,
                                  COMP_ATOM_DATA *composite_norm_data,
                                  INF_ATOM_DATA *inf_norm_at_data,
                                  int bIsotopic, int bTautomeric,
                                  PINChI2 *pINChI2,
                                  PINChI_Aux2 *pINChI_Aux2,
                                  int bAbcNumbers,
                                  INCHI_MODE nMode )
{
    int i, num_components, j, k, ret;
    inp_ATOM *inp_norm_at;
    INChI     *pINChI;
    INChI_Aux *pINChI_Aux;
    int      num_inp_at, num_at, num_H, offset, offset_H, next_offset, next_offset_H;

    if (composite_norm_data && inf_norm_at_data && ( bTautomeric == TAUT_INI || (pINChI2 && pINChI_Aux2) )) /* djb-rwth: addressing LLVM warning */
    {
        composite_norm_data += bTautomeric;
        inp_norm_at = composite_norm_data->at;
        num_components = composite_norm_data->num_components;
        offset = 0;
        offset_H = composite_norm_data->num_at - composite_norm_data->num_removed_H;
        if (bTautomeric == TAUT_INI)
        {
            ret = FillOutInputInfAtom( composite_norm_data->at, inf_norm_at_data, composite_norm_data->num_at,
                                       composite_norm_data->num_removed_H, 0 /*bAdd_DT_to_num_H*/,
                                       composite_norm_data->nNumRemovedProtons,
                                       composite_norm_data->nNumRemovedProtonsIsotopic,
                                       bIsotopic, bAbcNumbers );
            return ret;
        }
        else
        {
            for (i = 0; i < num_components; i++)
            {
                j = inchi_min( bTautomeric, TAUT_YES );
                /* count isotopic H on removed atoms -- isolated H(+) cations */
                inf_norm_at_data->nNumRemovedProtons += pINChI_Aux2[i][j]->nNumRemovedProtons;
                if (bIsotopic && bTautomeric == TAUT_YES)
                {
                    for (k = 0; k < NUM_H_ISOTOPES; k++)
                    {
                        if (pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k])
                        {
                            inf_norm_at_data->num_iso_H[k] += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k];
                            inf_norm_at_data->num_removed_iso_H += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k];
                        }
                    }
                }
                /* ignore deleted components */
                if (pINChI2[i][j] && pINChI2[i][j]->bDeleted)
                {
                    continue;
                }
                if (!pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms)
                {
                    j = ALT_TAUT( j );
                    if (!pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms)
                    {
                        continue; /* error ??? */
                    }
                }
                pINChI = pINChI2[i][j];
                pINChI_Aux = pINChI_Aux2[i][j];
                next_offset = composite_norm_data->nOffsetAtAndH[2 * i];
                next_offset_H = composite_norm_data->nOffsetAtAndH[2 * i + 1];
                num_at = next_offset - offset;
                if (num_at <= 0)
                    continue;
                num_H = next_offset_H - offset_H;
                num_inp_at = num_at + num_H;
                if (num_at != pINChI->nNumberOfAtoms || num_at != pINChI_Aux->nNumberOfAtoms)
                {
                    return 0; /* error */
                }
                ret = FillOutOneCanonInfAtom( pCG,
                                               inp_norm_at, inf_norm_at_data,
                                               inf_norm_at_data->pStereoFlags + i + 1,
                                               num_inp_at,
                                               offset, offset_H, bIsotopic,
                                               pINChI, pINChI_Aux, bAbcNumbers, nMode );
                if (ret)
                    return 0; /* error */

                inf_norm_at_data->StereoFlags |= inf_norm_at_data->pStereoFlags[i + 1];
                offset = next_offset;
                offset_H = next_offset_H;
            }
        }
        MakeRemovedProtonsString( inf_norm_at_data->nNumRemovedProtons, inf_norm_at_data->num_iso_H, NULL, bIsotopic,
                                  inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H );
    }

    return 1;
}
#endif /* } ifndef COMPILE_ANSI_ONLY */


/****************************************************************************/
int CheckCanonNumberingCorrectness( int num_atoms,
                                    int num_at_tg,
                                    sp_ATOM *at,
                                    CANON_STAT *pCS,
                                    CANON_GLOBALS *pCG,
                                    int bTautomeric,
                                    char *pStrErrStruct )
{
    int i, ret = 0;
    AT_NUMB *pCanonOrd = NULL;
    int nErrorCode = 0;
    AT_NUMB *pCanonRank; /* canonical ranks of the atoms or tautomeric groups */
    AT_NUMB *pCanonRankAtoms = NULL;

    /* djb-rwth: removing redundant code */

    pCanonRankAtoms = (AT_NUMB *) inchi_calloc( (long long)num_at_tg + 1, sizeof( pCanonRankAtoms[0] ) ); /* djb-rwth: cast operator added */

    /*
        Non-isotopic part
    */

    pCanonOrd = pCS->nLenCanonOrdStereo > 0 ? pCS->nCanonOrdStereo :
        pCS->nLenCanonOrd > 0 ? pCS->nCanonOrd : NULL;
    pCanonRank = pCanonRankAtoms;
    if (pCanonOrd && pCanonRank)
    {
        for (i = 0; i < num_at_tg; i++)
        {
            pCanonRank[pCanonOrd[i]] = (AT_NUMB) ( i + 1 );
        }
        ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS,
                                  pCG,
                                  0 );
        if (ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/)
        {
            nErrorCode |= WARN_FAILED_STEREO;
        }
    }
    else
    {
        nErrorCode |= ERR_NO_CANON_RESULTS;
        goto exit_function;
    }

    /*
        Isotopic part
    */

    pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0 ? pCS->nCanonOrdIsotopicStereo :
        pCS->nLenCanonOrdIsotopic > 0 ? pCS->nCanonOrdIsotopic : NULL;
    pCanonRank = pCanonRankAtoms;

    if (pCanonOrd && pCanonRank)
    {
        for (i = 0; i < num_at_tg; i++)
        {
            pCanonRank[pCanonOrd[i]] = (AT_NUMB) ( i + 1 );
        }
        ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS,
                                  pCG,
                                  0 );
        if (ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/)
        {
            nErrorCode |= ( pCS->nLenCanonOrdIsotopicStereo ? WARN_FAILED_ISOTOPIC_STEREO : WARN_FAILED_ISOTOPIC );
        }
    }

exit_function:
    if (pCanonRankAtoms)
    {
        inchi_free( pCanonRankAtoms );
    }

    if (nErrorCode)
    {
        return CT_CANON_ERR;
    }

    return 0;
}
