/*
 * 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
 *
*/


/*
Balanced Network Search

Normalization related procedures
*/

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

#include "mode.h"
#include "ichitime.h"
#include "ichicant.h"
#include "ichierr.h"
#include "ichitaut.h"
#include "ichinorm.h"
#include "util.h"
#include "ichister.h"
#include "ichi_bns.h"

#include "bcf_s.h"

#include <string.h>
#include "logging.h"                   /*(@nnuk : Nauman Ullah Khan) :: Needed for logging functionality*/

#define BNS_MARK_ONLY_BLOCKS        1  /* 1 => find only blocks, do not search for ring systems */
#define ALLOW_ONLY_SIMPLE_ALT_PATH  0  /* 0 => allow alt. path to contain same bond 2 times (in opposite directions) */
#define CHECK_TG_ALT_PATH           0  /* 1=> when chacking alt path of a tautomeric atom modify
t-group, not the atom */
/* 0=> old mode */

#define FIX_CPOINT_BOND_CAP         1  /* 1=> fix bug in case of double bond from neutral cpoint */
#define RESET_EDGE_FORBIDDEN_MASK   1  /* 1: previous; 0: do not apply "edge->forbidden &= pBNS->edge_forbidden_mask" */
#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS)     (EDGE_FORBIDDEN)
#else
#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS)     (EDGE_FORBIDDEN & PBNS->edge_forbidden_mask)
#endif


typedef enum tagAtTypeTotals
{
    /* counts do not include:
    charged atom adjacent to another charged atom
    atom in an unusual valence state or adjacent to an atom in an unusual valence state
    radicals different from singlet
    */
    /*ATTOT_NUM_Plus. */  /* number of positive charges, +1, is (ATTOT_NUM_CHARGES + ATTOT_TOT_CHARGE)/2 */
                          /*ATTOT_NUM_Minus.*/  /* number of negative charges, -1, is (ATTOT_NUM_CHARGES - ATTOT_TOT_CHARGE)/2 */
    ATTOT_NUM_NP_Plus,    /*  0 no H: =N(+)=, #N(+)-, =N(+)<, does not include onium cations >P(+)<, >N(+)< */
    ATTOT_NUM_NP_Proton,  /*  1 H(+): -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+), N=N,P */
    ATTOT_NUM_NP_H,       /*  2 H:    -NH2, =NH, >NH -NH(-) */
    ATTOT_NUM_N_Minus,    /*  3 (-):  -NH(-), >N(-), =N(-) */
    ATTOT_NUM_NP,         /*  4 no H: >N- =N-, #N  */
    ATTOT_NUM_ON,         /*  5 -N=O: do not allow -N=O => -NH-OH during H(+) add/removal */
    ATTOT_NUM_COH,        /*  6 =C-OH, #C-OH; O=O,S,Se,Te */
    ATTOT_NUM_CSH,        /*  7 -C-SH, -C-SeH -C-TeH  */
    ATTOT_NUM_ZOH,        /*  8 =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge, Z != C */
    ATTOT_NUM_OOH,        /*  9  -O-OH, O=O,S,Se,Te */
    ATTOT_NUM_ZOOH,       /* 10  O=Z-OH, O=O,S,Se,Te */
    ATTOT_NUM_NOH,        /* 11  =N-OH, -N(-)-OH */
    ATTOT_NUM_N_OH,       /* 12 >N-OH, -NH-OH, >NH(+)-OH, -N(-)-OH */
    ATTOT_NUM_CO,         /* 13 -C=O, =C=O; O=O,S,Se,Te  */
    ATTOT_NUM_ZO,         /* 14 -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge  */
    ATTOT_NUM_NO,         /* 15 -N=O, =N(+)=O */
    ATTOT_NUM_N_O,        /* 16  >N(+)=O, =N(+)=O */
    ATTOT_NUM_CO_Minus,   /* 17 =C-O(-), #C-O(-); O=O,S,Se,Te  */
    ATTOT_NUM_CS_Minus,   /* 18 -C-S(-); S = S, Se, Te */
    ATTOT_NUM_ZO_Minus,   /* 19 =Z-O(-), #Z-O(-); O = O, S, Se, Te */
    ATTOT_NUM_OO_Minus,   /* 20 -O-O(-), O=O,S,Se,Te */
    ATTOT_NUM_ZOO_Minus,  /* 21 O=Z-O(-), O=O,S,Se,Te */
    ATTOT_NUM_NO_Minus,   /* 22 >N-O(-), -NH-O(-) */
    ATTOT_NUM_N_O_Minus,  /* 23 -NH-O(-), >N-O(-); O = O, S, Se, Te */
    ATTOT_NUM_O_Minus,    /* 24 -Z-O(-); O=O,S,Se,Te */
    ATTOT_NUM_OH_Plus,    /* 25 any OH(+) */
    ATTOT_NUM_O_Plus,     /* 26 any O(+) without H */
    ATTOT_NUM_Proton,     /* 27 proton */
    ATTOT_NUM_HalAnion,   /* 28 Halogen anion */
    ATTOT_NUM_HalAcid,    /* 29 Halogen acid */
    ATTOT_NUM_Errors,     /* 30 for debugging */
    ATTOT_TOT_CHARGE,     /* 31 total of positive and negative single charges, +1 and -1 */
    ATTOT_NUM_CHARGES,    /* 32 number of positive and negative single charges, +1 and -1 */
    ATTOT_ARRAY_LEN       /* 33 array length */
} AT_TYPE_TOTALS;

#define ATBIT_NP_Plus    (1 << ATTOT_NUM_NP_Plus)
#define ATBIT_NP_Proton  (1 << ATTOT_NUM_NP_Proton)
#define ATBIT_NP_H       (1 << ATTOT_NUM_NP_H)
#define ATBIT_N_Minus    (1 << ATTOT_NUM_N_Minus)
#define ATBIT_NP         (1 << ATTOT_NUM_NP)
#define ATBIT_ON         (1 << ATTOT_NUM_ON)
#define ATBIT_COH        (1 << ATTOT_NUM_COH)
#define ATBIT_CSH        (1 << ATTOT_NUM_CSH)
#define ATBIT_ZOH        (1 << ATTOT_NUM_ZOH)
#define ATBIT_OOH        (1 << ATTOT_NUM_OOH)
#define ATBIT_ZOOH       (1 << ATTOT_NUM_ZOOH)
#define ATBIT_NOH        (1 << ATTOT_NUM_NOH)
#define ATBIT_N_OH       (1 << ATTOT_NUM_N_OH)
#define ATBIT_CO         (1 << ATTOT_NUM_CO)
#define ATBIT_ZO         (1 << ATTOT_NUM_ZO)
#define ATBIT_NO         (1 << ATTOT_NUM_NO)
#define ATBIT_N_O        (1 << ATTOT_NUM_N_O)
#define ATBIT_CO_Minus   (1 << ATTOT_NUM_CO_Minus)
#define ATBIT_CS_Minus   (1 << ATTOT_NUM_CS_Minus)
#define ATBIT_ZO_Minus   (1 << ATTOT_NUM_ZO_Minus)
#define ATBIT_OO_Minus   (1 << ATTOT_NUM_OO_Minus)
#define ATBIT_ZOO_Minus  (1 << ATTOT_NUM_ZOO_Minus)
#define ATBIT_NO_Minus   (1 << ATTOT_NUM_NO_Minus)
#define ATBIT_N_O_Minus  (1 << ATTOT_NUM_N_O_Minus)
#define ATBIT_O_Minus    (1 << ATTOT_NUM_O_Minus)
#define ATBIT_OH_Plus    (1 << ATTOT_NUM_OH_Plus)
#define ATBIT_O_Plus     (1 << ATTOT_NUM_O_Plus)
#define ATBIT_Proton     (1 << ATTOT_NUM_Proton)
#define ATBIT_HalAnion   (1 << ATTOT_NUM_HalAnion)
#define ATBIT_HalAcid    (1 << ATTOT_NUM_HalAcid)


#define ATBIT_Errors     (1 << ATTOT_NUM_Errors)

typedef struct tagProtonRemovalMaskAndType
{
    int typePos;  /* atoms accessible to positive charges */
    int maskPos;
    int typeNeg; /* atoms accessible to negative charges */
    int maskNeg;
    int typeH;     /* atoms accessible to hydrogen atoms */
    int maskH;
} PRMAT;

#define PR_SIMPLE_MSK  (ATBIT_NP_Proton | ATBIT_OH_Plus)
#define PR_SIMPLE_TYP  (ATT_ATOM_N | ATT_ATOM_P | ATT_O_PLUS)

#define ATBIT_MSK_NP   (ATBIT_NP_Plus | ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_N_Minus | ATBIT_NP)
#define KNOWN_ACIDIC_TYPE   (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO)
#define ATBIT_MSK_OS   (ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH |\
                        ATBIT_CO | ATBIT_ZO  | ATBIT_NO | ATBIT_N_O |\
                        ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_ZO_Minus | ATBIT_OO_Minus |\
                        ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_N_O_Minus /*| ATBIT_O_Minus*/ )
#define ATBIT_MSK_H    (ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH |\
                        ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH)

#define ATTYP_OS    (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO /*| ATT_OTHER_NEG_O*/ | ATT_OTHER_ZO)
#define ATTYP_NP    (ATT_ATOM_N | ATT_ATOM_P)
#define ATTYP_N     (ATT_ATOM_N)
#define ATTYP_P     (ATT_ATOM_P)

/************* simple proton removal from acids **************************/
#define AR_ANY_OH       0  /* 1 => create unknown to be acidic anions */
#define AR_SIMPLE_STEPS 3
/* acidic groups for proton removal, step 1 */
#define AR_SIMPLE_MSK1  (ATBIT_COH | ATBIT_CSH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_HalAcid)
#define AR_SIMPLE_TYP1  (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_HalAcid)
/* acidic groups for proton removal, step 2 */
#define AR_SIMPLE_MSK2  (AR_ANY_OH? (ATBIT_N_OH)  :0)
#define AR_SIMPLE_TYP2  (AR_ANY_OH? (ATT_N_O)     :0)
/* acidic groups for proton removal, step 3 */
#define AR_SIMPLE_MSK3  (AR_ANY_OH? (ATBIT_ZOH)   :0)
#define AR_SIMPLE_TYP3  (AR_ANY_OH? (ATT_OTHER_ZO):0)

/************* simple proton addition to acids **************************/
#define AA_ANY_O_Minus  0  /* 1 => neutralize unknown to be acidic anions */
#define AA_SIMPLE_STEPS 3
/* acidic groups for proton addition, step 1 */
#define AA_SIMPLE_MSK1  (ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_OO_Minus | ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_O_Minus | ATBIT_HalAnion)
#define AA_SIMPLE_TYP1  (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_OH_MINUS | ATT_HalAnion )
/* acidic groups for proton addition, step 2 */
#define AA_SIMPLE_MSK2  (AA_ANY_O_Minus? (ATBIT_N_O_Minus)               :0)
#define AA_SIMPLE_TYP2  (AA_ANY_O_Minus? (ATT_N_O)                       :0)
/* acidic groups for proton addition, step 3 */
#define AA_SIMPLE_MSK3  (AA_ANY_O_Minus? (ATBIT_ZO_Minus | ATBIT_O_Minus):0)
#define AA_SIMPLE_TYP3  (AA_ANY_O_Minus? (ATT_OTHER_ZO)                  :0)

#if ( FIX_NP_MINUS_BUG == 1 )
/* allow to add H(+) to =N(-) which previously was #N */
#undef AA_SIMPLE_STEPS
#define AA_SIMPLE_STEPS 4
#define AA_SIMPLE_MSK4  ATBIT_N_Minus
#define AA_SIMPLE_TYP4  ATT_NP_MINUS_V23
#endif

/************* hard proton removal from NP **************************/
/* (+) charge group for proton removal: mask & type */
#define PR_HARD_MSK_POS   ATBIT_MSK_NP
#define PR_HARD_TYP_POS   ATTYP_N
#define PR_HARD_TYP_POSP  ATTYP_P
/* (-) charge group for proton removal */
#define PR_HARD_MSK_NEG   (ATBIT_MSK_NP | ATBIT_MSK_OS)
#define PR_HARD_TYP_NEG   (ATTYP_N | ATTYP_OS)
/* H-group for proton removal */
#define PR_HARD_MSK_H     (ATBIT_MSK_NP | ATBIT_MSK_OS)
#define PR_HARD_TYP_H     (ATTYP_N | ATTYP_OS)

/************* hard proton removal from acids **************************/
/* (+) charge group for proton removal: mask & type */
#define AR_HARD_MSK_POS   ATBIT_MSK_NP
#define AR_HARD_TYP_POS   ATTYP_N
/* (-) charge group for proton removal */
#define AR_HARD_MSK_NEG   (ATBIT_MSK_NP | ATBIT_MSK_OS)
#define AR_HARD_TYP_NEG   (ATTYP_N | ATTYP_OS)
/* H-group acid for proton removal */
#define AR_HARD_MSK_HA    (ATBIT_CO | ATBIT_NO )
#define AR_HARD_TYP_HA    (ATT_ACIDIC_CO | ATT_NO)
/* H-group non-acid for proton removal */
#define AR_HARD_MSK_HN    ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~AR_HARD_MSK_HA)
#define AR_HARD_TYP_HN    ((ATTYP_N | ATTYP_OS) /*& ~AR_HARD_TYP_HA*/)

/************* hard proton addition to acids **************************/
/* (+) charge group for proton removal: mask & type */
#define AA_HARD_MSK_POS   ATBIT_MSK_NP
#define AA_HARD_TYP_POS   ATTYP_N
/* (-) charge group for negative charge removal */
#define AA_HARD_MSK_NEG   ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~(ATBIT_CO | ATBIT_NO ))
#define AA_HARD_TYP_NEG   (ATTYP_N | ATTYP_OS)
/* (-) charge group to accept negative charges  */
#define AA_HARD_MSK_CO    (ATBIT_CO | ATBIT_NO )
#define AA_HARD_TYP_CO    (ATT_ACIDIC_CO | ATT_NO)
/* H-group non-acid for proton removal */
#define AA_HARD_MSK_H     (ATBIT_MSK_NP | ATBIT_MSK_OS)
#define AA_HARD_TYP_H     (ATTYP_N | ATTYP_OS)


/*****************************************************************************/
#define BNS_MAX_NUM_FLOW_CHANGES (1+2*MAX_BOND_EDGE_CAP)

/* -- opiginal Pascal values --
#define NO_VERTEX     0
#define BLOSSOM_BASE -1
#define FIRST_INDX    1
*/

#define TREE_NOT_IN_M  0  /* not in T or T' */
#define TREE_IN_2      1  /* in T' and not s-reachable */
#define TREE_IN_2BLOSS 2  /* in T' and in a blossom, is s-reachable */
#define TREE_IN_1      3  /* in T and is s-reachable */

#define TREE_IS_S_REACHABLE(X) (Tree[X] >= TREE_IN_2BLOSS)
#define TREE_IS_ON_SCANQ TREE_IS_S_REACHABLE
/* #define TREE_IS_ON_SCANQ(X)    (Tree[X] != TREE_NOT_IN_M) */
#define TREE_MARK(X, MARK)           do{ if( Tree[X] < MARK ) Tree[X]=MARK; }while(0)


/*****************************************************************************
*  store changes done to check whether an alternating path exists
*  (see bSetBnsToCheckAltPath, bRestoreBnsAfterCheckAltPath)
******************************************************************************/
typedef struct tagAltPathChanges
{
    /* caps changed in up to 2 vertices */
    VertexFlow nOldCapsVert[2][MAXVAL + 1];
    Vertex     vOldVert[2];
    S_CHAR     bSetOldCapsVert[2]; /* number of caps to restore, including st-cap */
                                   /* save ids of the newly created temporary vertices */
    Vertex     vNewVertex[2];
    S_CHAR     bSetNew[2];         /* indicators whether to remove vertices */
} ALT_PATH_CHANGES;



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


/* Local functions */

int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at );
int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 );
int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms );
int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 );
int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond );
int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow );
int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow );
int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow );
int CompTGroupNumber( const void *tg1, const void *tg2, void *p );
int CompCGroupNumber( const void *cg1, const void *cg2, void *p );

/* Rings, Blocks, Non-stereo bonds */
int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo );
int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo );
int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo );

/* Called from BalancedNetworkSearch */
int GetVertexDegree( BN_STRUCT* pBNS, Vertex v );
/* Vertex Get2ndNeighbor1( BN_STRUCT* pBNS, Vertex u, EdgeIndex iedge ); not used */
Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv );
Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge );
int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t );
int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow );
int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv );
int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv );
Vertex FindBase( Vertex u, Vertex *BasePtr );
int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen );
Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize,
                    Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv,
                    Edge *SwitchEdge, Vertex *BasePtr,
                    Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree );
int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow );
int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta );

/*
int SetBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow );
int SetBondsRestoreBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow );
*/
int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 );
int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 );

int nMinFlow2Check( BN_STRUCT *pBNS, int iedge );
int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge );
int nCurFlow2Check( BN_STRUCT *pBNS, int iedge );

/* Bonds testing */
/*
int bRestoreFlowToCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow );
*/
int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd );
int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd );
int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow );
int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest );
/* 
int bIsAltBond(int bond_type); -- djb-rwth: function definition not found 
*/

/* Fix bonds */
int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask );
int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
int fix_explicitly_indicated_bonds( int nebend, int *ebend, BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );

/* Alt path testing */
int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type,
                           int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots );
int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow );
Vertex GetGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type );
BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type );
int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots );
int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow );
int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors );

static void remove_alt_bond_marks( inp_ATOM *at, int num_atoms );
int bIsBnsEndpoint( BN_STRUCT *pBNS, int v );

/* Protons removal, charge neutralization */
/* int is_acidic_CO(inp_ATOM* atom, int at_no); */ /* djb-rwth: function definition not found*/
int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] );
int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract );
int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark );
int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms );
int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info );
int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms,
                                  int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract );
int is_Z_atom( U_CHAR el_number );
int IsZOX( inp_ATOM *atom, int at_x, int ord );
int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info );
int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms,
                            BN_STRUCT *pBNS, int nType, int nMask, int nCharge );
int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms,
                            BN_STRUCT *pBNS, int nType, int nMask );
int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS );
int SetInitCapFlowToCurrent( BN_STRUCT *pBNS );
int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove );
int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add );
int HardRemoveAcidicProtons( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove,
                             int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD );
int HardAddAcidicProtons( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add,
                          int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD );
int HardRemoveHplusNP( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges,
                       BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD );
int RemoveNPProtonsAndAcidCharges( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD );
Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv );
int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v );
int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge );
int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v );
int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge );

int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge );
int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType );
int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType );

#if ( BNS_RAD_SEARCH == 1 )
int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u );
int cmp_rad_endpoints( const void *a1, const void *a2 );
int cmp_endpoints_rad( const void *a1, const void *a2 );
#endif

int bHasChargedNeighbor( inp_ATOM *at, int iat );
/*****************************************************************************/
/**** prim(v) is v' *****/
#define prim(v) (Vertex)((v)^1)

/*****************************************************************************/
#define SwitchEdge_Vert1(u) SwitchEdge[u][0]
#define SwitchEdge_Vert2(u) Get2ndEdgeVertex( pBNS, SwitchEdge[u] )
#define SwitchEdge_IEdge(u) SwitchEdge[u][1]
/*****************************************************************************/





/****************************************************************************
Returns value > 0 if a bond has been changed
****************************************************************************/
int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow )
{
    /*flow1 = edge->flow;*/ /* output from BNS */
    switch (bChangeFlow & BNS_EF_CHNG_RSTR)
    {
        case 0:
            /* the flow has not been permitted to change inside the BNS */
            /* nothing to do */
            /*flow1 = edge->flow;*/    /* output from BNS, the original flow value */
                                       /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */
            break;
        case BNS_EF_CHNG_FLOW:
            /* the flow has been changed by the BNS; update flow0 */
            /*flow2 = edge->flow;*/    /* output from BNS, the changed value */
                                       /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */
            edge->flow0 = edge->flow;   /* SAVE NEW EDGE FLOW AS THE INITIAL FLOW FROM CHEM. BONDS */
            break;
        case BNS_EF_CHNG_RSTR:
            /* the flow has been changed by the BNS; requested to change it back */
            /*flow2 = edge->flow;*/    /* output from BNS, the changed value */
                                       /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */
            edge->flow = edge->flow - delta;    /* CHANGE EDGE FLOW BACK (RESTORE) */
            break;
        case BNS_EF_RSTR_FLOW:
            /* the flow has not been permitted to change inside the BNS */
            /* nothing to do */
            /*flow1 = edge->flow;*/    /* output from BNS, the original flow value */
                                       /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */
            break;
    }

    return 0;
}


/****************************************************************************
Returns value > 0 if a bond has been changed; do not change flow
****************************************************************************/
int SetAtomBondType( BNS_EDGE *edge,
                     U_CHAR *bond_type12,
                     U_CHAR *bond_type21,
                     int delta,
                     int bChangeFlow )
{
    int    flow1, flow2, tmp, ret = 0;
    int   bond_mark = 0, bond_type, new_bond_type; /* djb-rwth: addressing LLVM warning */

    if (!edge->pass || !bond_type21)
    {
        return 0;
    }

    switch (bChangeFlow & BNS_EF_CHNG_RSTR)
    {
        case 0:                    /* the flow has not been permitted to change inside the BNS: simulated in case of check one bond */
        case BNS_EF_RSTR_FLOW:     /* the flow has not been permitted to change inside the BNS: obsolete mode, unexpected bChangeFlow */
            flow1 = edge->flow0;   /* output from BNS, the original (old) flow value */
            flow2 = flow1 + delta; /* the flow would be changed to this value by the BNS if permitted */
            break;
        case BNS_EF_CHNG_FLOW:     /* the flow has been changed by the BNS */
        case BNS_EF_CHNG_RSTR:     /* the flow has been changed by the BNS; requested to change it back */
            flow2 = edge->flow;    /* output from BNS, the changed (new) value */
            flow1 = edge->flow0;   /* the original flow (old) value before the BNS */
            break;
        default:
            return 0; /* added 2006-03-21 */
    }

    if (( bChangeFlow & BNS_EF_CHNG_BONDS ) && ( bChangeFlow & BNS_EF_ALTR_NS ) != BNS_EF_ALTR_NS)
    {
        /* Set new bond types according to the new flow values */
        new_bond_type = flow2 + BOND_SINGLE;
        if (*bond_type12 != new_bond_type)
        {
            *bond_type12 = *bond_type21 = new_bond_type;
            ret++;
        }
    }
    else
    {
        if (bChangeFlow & BNS_EF_ALTR_BONDS)
        {
            if (flow1 == flow2)
            {
                goto exit_function;
            }
            /* Update alternating bond information */
            if (flow1 > flow2)
            {
                /* Make sure flow2 > flow1 */
                tmp = flow1;
                flow1 = flow2;
                flow2 = tmp;
            }
            /* djb-rwth: removing redundant code */
            switch (bond_type = ( *bond_type12 & BOND_TYPE_MASK ))
            {
                case BOND_SINGLE:
                case BOND_DOUBLE:
                case BOND_TRIPLE:
                    /* assume that the input bond type fits either flow1 or flow2 */
                    if (flow1 == 0 && flow2 == 1)
                    {
                        if (bChangeFlow & BNS_EF_SET_NOSTEREO)
                        {
                            bond_mark = BOND_MARK_ALT12NS;
                            bond_type = BOND_ALT12NS;
                        }
                        else
                        {
                            bond_mark = BOND_MARK_ALT12;
                            bond_type = BOND_ALTERN;
                        }
                    }
                    else
                    {
                        if (flow1 == 0 && flow2 == 2)
                        {
                            bond_mark = BOND_MARK_ALT13;
                            bond_type = BOND_ALT_13;
                        }
                        else
                        {
                            if (flow1 == 1 && flow2 == 2)
                            {
                                bond_mark = BOND_MARK_ALT23;
                                bond_type = BOND_ALT_23;
                            }
                            else
                            {
                                return BNS_BOND_ERR; /* error */
                            }
                        }
                    }
                    break;
                case BOND_TAUTOM:
                    if (flow1 == 0 && flow2 == 1)
                    {
                        bond_mark = BOND_MARK_ALT12NS;
                    }
                    else
                        if (flow1 == 1 && flow2 == 2) {
                            bond_mark = BOND_MARK_ALT23;
                        }
                        else {
                            return BNS_BOND_ERR; /* error */
                        }
                        break;
                default:
                    new_bond_type = bond_type;
                    bond_mark = ( *bond_type12 & BOND_MARK_MASK );
                    switch (bond_mark)
                    {
                        case BOND_MARK_ALT12:
                            if (( bChangeFlow & BNS_EF_SET_NOSTEREO ) && flow1 == 0 && flow2 == 1)
                            {
                                bond_mark = BOND_MARK_ALT12NS;
                                new_bond_type = BOND_ALT12NS;
                                break;
                            }
                        case BOND_MARK_ALT12NS:
                            if (flow1 == 2 || flow2 == 2)
                            {
                                bond_mark = BOND_MARK_ALT123;
                                new_bond_type = BOND_ALT_123;
                            }
                            break;
                        case BOND_MARK_ALT13:
                            if (flow1 == 1 || flow2 == 1)
                            {
                                bond_mark = BOND_MARK_ALT123;
                                new_bond_type = BOND_ALT_123;
                            }
                            break;
                        case BOND_MARK_ALT23:
                            if (flow1 == 0 || flow2 == 0)
                            {
                                bond_mark = BOND_MARK_ALT123;
                                new_bond_type = BOND_ALT_123;
                            }
                            break;
                        case BOND_MARK_ALT123:
                            break;

                        case 0: /* special case: second alt bond testing */
                            if (flow1 == 0 && flow2 == 1)
                            {
                                bond_mark = BOND_MARK_ALT12;
                            }
                            else
                            {
                                if (flow1 == 0 && flow2 == 2)
                                {
                                    bond_mark = BOND_MARK_ALT13;
                                }
                                else
                                {
                                    if (flow1 == 1 && flow2 == 2)
                                    {
                                        bond_mark = BOND_MARK_ALT23;
                                    }
                                    else
                                    {
                                        return BNS_BOND_ERR; /* error */
                                    }
                                }
                            }
                            break;

                        default:
                            return BNS_BOND_ERR; /* error */
                    }

                    switch (bond_type)
                    {
                        case BOND_TAUTOM:
                            break;
                        case BOND_ALTERN:
                        case BOND_ALT12NS:
                        case BOND_ALT_123:
                        case BOND_ALT_13:
                        case BOND_ALT_23:
                            bond_type = new_bond_type;
                            break;
                        default:
                            return BNS_BOND_ERR; /* error */
                    }
            }

            new_bond_type = bond_type | bond_mark;
            if (new_bond_type != *bond_type12)
            {
                *bond_type12 = *bond_type21 = new_bond_type;
                ret++;
            }
        }
    }

exit_function:

    return ret;
}


/****************************************************************************
Run BalancedNetworkSearch( ... ) until no aug pass is found
****************************************************************************/
int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow )
{
    int pass, delta = 0, nSumDelta;

    nSumDelta = 0;
    for (pass = 0; pass < pBNS->max_altp; pass++)
    {
        pBNS->alt_path = pBNS->altp[pass];
        pBNS->bChangeFlow = 0;
        delta = BalancedNetworkSearch( pBNS, pBD, bChangeFlow );
        ReInitBnData( pBD );
        if (0 < delta)
        {
            pBNS->num_altp++;
            nSumDelta += abs( delta );
        }
        else
        {
            break;
        }
    }

    if (IS_BNS_ERROR( delta ))
    {
        return delta;
    }

    if (bInchiTimeIsOver( pBNS->ic, pBNS->ulTimeOutTime ))
    {
        return BNS_TIMEOUT;
    }

    return nSumDelta; /* number of eliminated pairs of "dots"  */
}


/****************************************************************************/
int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS,
                                           inp_ATOM *atom,
                                           int v1 )
{
    BNS_VERTEX *vert = pBNS->vert + v1;
    inp_ATOM *at = atom + v1;
    S_CHAR      cValue;
    int  nChanges = 0;

    /* Set only on the 1st pass */
    if (!vert->st_edge.pass)
    {
        return 0;
    }

    /* Adjust chem_bonds_valence */
    cValue = at->chem_bonds_valence - at->valence;
    if (cValue >= 0 && cValue != vert->st_edge.flow)
    {
        at->chem_bonds_valence = at->valence + vert->st_edge.flow;
        nChanges++;
    }

    /* Adjist radical */
    switch (vert->st_edge.cap - vert->st_edge.flow)
    {
        case 0:
            cValue = 0;
            break;
        case 1:
            cValue = RADICAL_DOUBLET;
            break;
        case 2:
            cValue = RADICAL_TRIPLET;
            break;
        default:
            return BNS_BOND_ERR;
    }
    if (cValue != at->radical)
    {
        at->radical = cValue;
        nChanges++;
    }

    return nChanges;
}


/****************************************************************************/
int AddChangedAtHChargeBNS( inp_ATOM *at,
                            int num_atoms,
                            int nAtTypeTotals[],
                            S_CHAR *mark )
{
    int i, mask, num;
    for (i = 0, num = 0; i < num_atoms; i++)
    {
        if (mark[i])
        {
            mark[i] = 0;
#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
            /* add ignoring adjacent charges */
            at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, -2 );
#else
            at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 );
#endif
            num++;
        }
    }

    return num;
}


/****************************************************************************
Eliminate neutral representation ambiguity:

replace (+)--N==(-) with (+)==N--(-)

here (+) is positive charge group,
(-) is negative charge group, N is N or P
This reduces possibility of creating ion pair -OH => -O(+) + H(+)
instead of removing H(+) from N or P

!Call this function after alt path was found and new flows have been set.
****************************************************************************/
int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms )
{
    int pass, i, v0, v1, v2, ineigh1, /*ineigh0,*/ /*ineigh2,*/
        vLast, n, delta, ret, err = 0;
    int nFound, k;
    BNS_EDGE *edge;

    for (pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass--)
    {

        pBNS->alt_path = pBNS->altp[pass];
        v1 = ALTP_START_ATOM( pBNS->alt_path );
        n = ALTP_PATH_LEN( pBNS->alt_path );
        delta = ALTP_DELTA( pBNS->alt_path );
        vLast = ALTP_END_ATOM( pBNS->alt_path );
        v0 = v2 = NO_VERTEX; /* negative number */

        for (i = 0; i < n; i++, delta = -delta, v0 = v1, v1 = v2 /*, ineigh0 = ineigh1*/)
        {
            ineigh1 = ALTP_THIS_ATOM_NEIGHBOR( pBNS->alt_path, i );  /* v1->v2 neighbor */
                                                                     /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/  /* v2->v1 neighbor */
            edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1];
            /* follow the BN Structure, not the inp_ATOM, to take care of swithching to
            t-groups, c-groups or other fictitious edges/vertices
            */
            v2 = edge->neighbor12 ^ v1;
            if (v1 < num_atoms &&
                ( (v0 >= num_atoms && ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP )) ||
                  (v2 >= num_atoms && ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) ))) /* djb-rwth: addressing LLVM warning */
            {
                int        cgPos = 0, cgNeg = 0;
                int        neighPos = -1, neighNeg = -1;
                BNS_EDGE *edgePos, *edgeNeg;
                nFound = 0;
                for (k = pBNS->vert[v1].num_adj_edges - 1; k >= 0 && ( neighPos < 0 || neighNeg < 0 ); k--)
                {
                    BNS_EDGE   *next_edge = pBNS->edge + pBNS->vert[v1].iedge[k];
                    int         v = next_edge->neighbor12 ^ v1;
                    if (pBNS->vert[v].type & BNS_VERT_TYPE_C_GROUP)
                    {
                        if (pBNS->vert[v].type & BNS_VERT_TYPE_C_NEGATIVE)
                        {
                            cgNeg = v;
                            neighNeg = k;
                            nFound++;
                        }
                        else
                        {
                            cgPos = v;
                            neighPos = k;
                            nFound++;
                        }
                    }
                }
                if (2 == nFound && neighPos >= 0 && neighNeg >= 0)
                {
                    /* both c-groups have been found */
                    edgePos = pBNS->edge + pBNS->vert[v1].iedge[neighPos];
                    edgeNeg = pBNS->edge + pBNS->vert[v1].iedge[neighNeg];
                    if (edgePos->flow < edgeNeg->flow)
                    {
                        /* ambiguity found; replace (+cg)--N==(-cg) with (+cg)==N--(-cg) */
                        int dflow = edgeNeg->flow - edgePos->flow;

                        edgePos->flow += dflow;
                        pBNS->vert[cgPos].st_edge.cap += dflow;
                        pBNS->vert[cgPos].st_edge.flow += dflow;

                        edgeNeg->flow -= dflow;
                        pBNS->vert[cgNeg].st_edge.cap -= dflow;
                        pBNS->vert[cgNeg].st_edge.flow -= dflow;
                        ret++;
                    }
                }
            }
        }

        if (v2 != vLast)
        {
            err = BNS_PROGRAM_ERR;
        }
    }

    return err ? err : ret;
}


/****************************************************************************
Add or remove ixplicit or implicit hydrogens
****************************************************************************/
int AddOrRemoveExplOrImplH( int nDelta,
                            inp_ATOM *at,
                            int num_atoms,
                            AT_NUMB at_no,
                            T_GROUP_INFO *t_group_info )
{
    int i, iso, tot_num_iso_H,
        num_H,                  /* number of H before the removal, including explicit H  */
        nNum2Remove,            /* number of H to remove */
        nNumRemovedExplicitH; /* djb-rwth: removing redundant variables */
    S_CHAR    num_iso_H[NUM_H_ISOTOPES];
    inp_ATOM *at_H;

    if (!nDelta)
    {
        return 0;
    }
    /* add */
    if (nDelta > 0)
    {
        at[at_no].num_H += nDelta;
        t_group_info->tni.nNumRemovedProtons--;
        return nDelta;
    }
    /* remove */
    nNum2Remove = -nDelta;
    nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; /* number of explicit H saved separately in
                                                                   at[num_atoms+i], i=0..nNumRemovedExplicitH-1 */
    tot_num_iso_H = NUM_ISO_H( at, at_no );
    num_H = at[at_no].num_H;
    /*
    tot_num_iso_H          = NUM_ISO_H(at,at_no);
    num_H                  = at[at_no].num_H;
    nNumAtomExplicitH      = 0;
    nNumRemovedExplicitH   = t_group_info->tni.nNumRemovedExplicitH;
    tot_num_explicit_iso_H = 0;
    */

    at_H = at + num_atoms;
    memcpy(num_iso_H, at[at_no].num_iso_H, sizeof(num_iso_H));
    /*  Remove all explicit H, otherwise a false stereo can occur.
    Example: remove H(+) from the following substructure:

    H                                            H
    A      /                                      A     /
    >X==N(+)    produces false stereogenic bond:  >X==N
    B      \                                      B
    H

    To avoid this effect all explicit H atoms must be removed
    */

    /* djb-rwth: removing redundant code */
    for (i = 0; i < nNumRemovedExplicitH; )
    {
        if (at_H[i].neighbor[0] == at_no)
        {
            int m, k, orig_no = at_H[i].orig_at_number;
            nNumRemovedExplicitH--;
            /* djb-rwth: removing redundant code */
            if (nNumRemovedExplicitH > i)
            {
                inp_ATOM at_i = at_H[i];
                memmove(at_H + i, at_H + i + 1, sizeof(at_H[0]) * ((long long)nNumRemovedExplicitH - i)); /* djb-rwth: cast operator added */
                at_H[nNumRemovedExplicitH] = at_i; /* save removed H (for debugging purposes?) */
            }
            /* Adjust 0D parities */
            if (at[at_no].sb_parity[0])
            {
                for (m = 0; m < MAX_NUM_STEREO_BONDS && at[at_no].sb_parity[m]; m++)
                {
                    if (at[at_no].sn_orig_at_num[m] == orig_no)
                    {
                        if (at[at_no].valence >= MIN_NUM_STEREO_BOND_NEIGH)
                        {
                            at[at_no].sn_ord[m] = k = ( at[at_no].sb_ord[m] == 0 );
                            at[at_no].sn_orig_at_num[m] = at[(int) at[at_no].neighbor[k]].orig_at_number;
                            if (ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ))
                            {
                                at[at_no].sb_parity[m] = 3 - at[at_no].sb_parity[m];
                            }
                        }
                        else
                        {
                            at[at_no].sn_ord[m] = -99; /* no sb neighbor exists anymore */
                            at[at_no].sn_orig_at_num[m] = 0;
                            if (ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ))
                            {
                                int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord;
                                if (0 < get_opposite_sb_atom( at, at_no, at[at_no].sb_ord[m],
                                                              &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ))
                                {
                                    at[at_no].sb_parity[m] =
                                        at[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = AB_PARITY_UNDF;
                                }
                            }
                        }
                    }
                }
            }
            /* do not increment i here: we have shifted next at_H[] element
            to the ith position and decremented nNumRemovedExplicitH */
        }
        else
        {
            i++;
        }
    }

    for (iso = -1; iso < NUM_H_ISOTOPES && 0 < nNum2Remove; iso++)
    {
        /* Each pass removes up to one H */
        if (iso < 0)
        {
            /* Try to remove non-isotopic */
            while (tot_num_iso_H < num_H && 0 < nNum2Remove)
            {
                /* Non-isotopic H exists */
                num_H--;
                t_group_info->tni.nNumRemovedProtons++;
                nNum2Remove--;
            }
        }
        else
        {
            /* Remove isotopic */
            while (num_iso_H[iso] && num_H && 0 < nNum2Remove)
            {
                /* Isotopic H exists */
                num_H--;
                num_iso_H[iso] --;
                t_group_info->tni.nNumRemovedProtonsIsotopic[iso] ++;
                t_group_info->tni.nNumRemovedProtons++;
                nNum2Remove--;
            }
        }
    }
#if ( bRELEASE_VERSION != 1 )
    if (nNum2Remove)
    {
        int stop = 1; /* <BRKPT> Program error */
    }
#endif
    if (nDelta + nNum2Remove < 0)
    {
        at[at_no].num_H = num_H;
        memcpy(at[at_no].num_iso_H, num_iso_H, sizeof(at[0].num_iso_H));
        t_group_info->tni.nNumRemovedExplicitH = nNumRemovedExplicitH;
    }

    return nDelta + nNum2Remove;
}


/****************************************************************************/
int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS,
                                  inp_ATOM *at,
                                  int num_atoms,
                                  int nAtTypeTotals[],
                                  S_CHAR *mark,
                                  T_GROUP_INFO *t_group_info,
                                  int bSubtract )
{
    int       pass, i, v0, v1, v2, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, err = 0;
    BNS_EDGE *edge;
    int       nDeltaH, nDeltaCharge;
    int       mask, type; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */

    for (pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass--)
    {

        pBNS->alt_path = pBNS->altp[pass];
        v1 = ALTP_START_ATOM( pBNS->alt_path );
        n = ALTP_PATH_LEN( pBNS->alt_path );
        delta = ALTP_DELTA( pBNS->alt_path );
        vLast = ALTP_END_ATOM( pBNS->alt_path );
        v0 = v2 = NO_VERTEX;

        for (i = 0; i < n; i++, delta = -delta, v0 = v1, v1 = v2)
        {
            ineigh1 = ALTP_THIS_ATOM_NEIGHBOR( pBNS->alt_path, i );  /* v1->v2 neighbor */
                                                                     /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/  /* v2->v1 neighbor */
            edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1];
            /* follow the BN Structure, not the inp_ATOM, to take care of swithching to
            t-groups, c-groups or other fictitious edges/vertices
            */
            v2 = edge->neighbor12 ^ v1;
            if (v1 < num_atoms && ( v0 >= num_atoms || v2 >= num_atoms ))
            {
                nDeltaH = nDeltaCharge = 0;
                if (v0 >= num_atoms)
                {
                    /* delta(v0-v1) = -delta(v1-v2) along the alternating path */
                    if (pBNS->vert[v0].type & BNS_VERT_TYPE_TGROUP)
                    {
                        nDeltaH -= delta;
                    }
                    else
                    {
                        if (pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP)
                        {
                            nDeltaCharge += delta;
                        }
                    }
                }
                if (v2 >= num_atoms)
                {
                    if (pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP)
                    {
                        nDeltaH += delta;
                    }
                    else
                    {
                        if (pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP)
                        {
                            nDeltaCharge -= delta;
                        }
                    }
                }
                if (nDeltaH || nDeltaCharge)
                {
                    if (bSubtract)
                    {
                        if (!mark[v1])
                        {
                            /* first time the atom has been encountered: subtract */
                            /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
                            type = GetAtomChargeType(at, v1, nAtTypeTotals, &mask, 2);
#else
                            type = GetAtomChargeType(at, v1, nAtTypeTotals, &mask, 1);
#endif
                            ret++; /* number of changed atoms */
                            mark[v1] ++;
                        }
                    }
                    else
                    {
                        /* Change */
                        at[v1].charge += nDeltaCharge;
                        if (nDeltaH)
                        {
                            AddOrRemoveExplOrImplH( nDeltaH, at, num_atoms, (AT_NUMB) v1, t_group_info );
                        }
                        ret++; /* number of changed atoms */
                    }
                }
            }
        }

        if (v2 != vLast)
        {
            err = BNS_PROGRAM_ERR;
        }
    }

    return err ? err : ret;
}


/****************************************************************************
Set Bonds From BnS Struct Flow
****************************************************************************/
int SetBondsFromBnStructFlow( BN_STRUCT *pBNS,
                              inp_ATOM *at,
                              int num_atoms,
                              int bChangeFlow0 )
{
    int       pass, i, v0, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, ret_val, err = 0;
    BNS_EDGE *edge;
    int       bChangeFlowAdd; /* djb-rwth: removing redundant variables */
    int       bChangeFlow = ( bChangeFlow0 & ~BNS_EF_SET_NOSTEREO );
    /*
    bCheckMovingRad = (bChangeFlow & BNS_EF_ALTR_NS) == BNS_EF_ALTR_NS &&
    pBNS->tot_st_cap > pBNS->tot_st_flow;
    */
    for (pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass--)
    {
        pBNS->alt_path = pBNS->altp[pass];
        v1 = ALTP_START_ATOM( pBNS->alt_path );
        n = ALTP_PATH_LEN( pBNS->alt_path );
        delta = ALTP_DELTA( pBNS->alt_path );
        vLast = ALTP_END_ATOM( pBNS->alt_path );
        if (( bChangeFlow0 & BNS_EF_SET_NOSTEREO ) &&
            ( pBNS->vert[v1].st_edge.cap0 > pBNS->vert[v1].st_edge.flow0 ||
              pBNS->vert[vLast].st_edge.cap0 > pBNS->vert[vLast].st_edge.flow0 ))
        {
            /* djb-rwth: removing redundant code */
            bChangeFlowAdd = BNS_EF_SET_NOSTEREO;
            ret |= 2;
        }
        else
        {
            bChangeFlowAdd = 0;
        }
        /* start vertex */
        if (( bChangeFlow & BNS_EF_CHNG_RSTR ) == BNS_EF_CHNG_RSTR)
        {
            /* Restore s-v1 edge flow to the BNS this pass input value */
            ; /*pBNS->vert[v1].st_edge.flow -= delta;*/
        }
        else
        {
            if (( bChangeFlow & BNS_EF_SAVE_ALL ) == BNS_EF_SAVE_ALL)
            {
                if (v1 < num_atoms)
                {
                    /* Will produce wrong result if called for v1 next time? */
                    ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v1 );
                    if (ret_val < 0)
                    {
                        err = BNS_PROGRAM_ERR;
                    }
                    else
                    {
                        ret |= ( ret_val > 0 );
                    }
                }
                /*pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow;*/
            }
        }

        pBNS->vert[v1].st_edge.pass = 0;

        v0 = v2 = NO_VERTEX;
        for (i = 0; i < n; i++, delta = -delta, v0 = v1, v1 = v2)
        {
            ineigh1 = ALTP_THIS_ATOM_NEIGHBOR( pBNS->alt_path, i );  /* v1->v2 neighbor */
            ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR( pBNS->alt_path, i );  /* v2->v1 neighbor */
            edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1];
            /* follow the BN Structure, not the inp_ATOM, to take care of swithching to
            t-groups, c-groups or other fictitious edges/vertices
            */

            v2 = edge->neighbor12 ^ v1;

            /* change at->chem_bonds_valence 2004-03-08 */
            if (( bChangeFlow & BNS_EF_CHNG_BONDS ) && v1 < num_atoms)
            {
                if (v0 >= num_atoms && v2 < num_atoms)
                {
                    at[v1].chem_bonds_valence += delta; /* change in v1-v2 bond order */
                }
                else
                {
                    if (v0 < num_atoms && v2 >= num_atoms && v0 != NO_VERTEX)
                    {
                        at[v1].chem_bonds_valence -= delta; /* change in v0-v1 bond order */
                    }
                }
            }

            if (!edge->pass)
            {
                continue;
            }

            if (v1 < num_atoms && ineigh1 < at[v1].valence &&
                 v2 < num_atoms && ineigh2 < at[v2].valence)
            {
                if (( bChangeFlow0 & BNS_EF_ALTR_NS ) == BNS_EF_ALTR_NS &&
                    ( bChangeFlow0 & BNS_EF_SAVE_ALL ) == BNS_EF_SAVE_ALL)
                {
                    /* 2004-07-02 special mode: save new ring bonds and mark as non-stereo non-ring bonds */
                    if (at[v1].nRingSystem != at[v2].nRingSystem)
                    {
                        /* Non-ring bond (bridge) */
                        bChangeFlowAdd = BNS_EF_ALTR_NS;
                    }
                    else
                    {
                        /* Ring bond */
                        bChangeFlowAdd = 0;
                    }
                }
                /* Change bonds on the first pass only: in this case all flow correspond to the BNS output */
                ret_val = SetAtomBondType( edge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], delta, bChangeFlow | bChangeFlowAdd );
                if (ret_val < 0)
                {
                    err = BNS_PROGRAM_ERR;
                }
                else
                {
                    ret |= ( ret_val > 0 );
                }
            }
            edge->pass = 0;
        }

        if (v2 != vLast)
        {
            err = BNS_PROGRAM_ERR;
        }
        else
        {
            if (( bChangeFlow & BNS_EF_CHNG_RSTR ) == BNS_EF_CHNG_RSTR)
            {
                /* Restore v2-t edge flow to the BNS this pass input value */
                /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */
                ; /*pBNS->vert[v2].st_edge.flow += delta; */
            }
            else
            {
                if (( bChangeFlow & BNS_EF_SAVE_ALL ) == BNS_EF_SAVE_ALL)
                {
                    if (v2 < num_atoms)
                    {
                        ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v2 );
                        if (ret_val < 0)
                        {
                            err = BNS_PROGRAM_ERR;
                        }
                        else
                        {
                            ret |= ( ret_val > 0 );
                        }
                    }
                    /*pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow;*/
                }
            }
        }
        pBNS->vert[v2].st_edge.pass = 0;
    }

    return err ? err : ret;
}


/****************************************************************************/
int MarkAtomsAtTautGroups( BN_STRUCT *pBNS,
                           int num_atoms,
                           BN_AATG *pAATG,
                           int nEnd1,
                           int nEnd2 )
{
    int       pass, i, j, v1, v2, ineigh1, ineigh2, vLast, vFirst, n, delta, err = 0; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
    BNS_EDGE *edge;
    S_CHAR    cDelta[MAX_ALT_AATG_ARRAY_LEN];
    AT_NUMB   nVertex[MAX_ALT_AATG_ARRAY_LEN];
    int       nLenDelta = 0, last_i, nNumFound;

    for (pass = pBNS->num_altp - 1; 0 <= pass; pass--)
    {
        pBNS->alt_path = pBNS->altp[pass];
        vFirst =
            v1 = ALTP_START_ATOM( pBNS->alt_path );
        n = ALTP_PATH_LEN( pBNS->alt_path );
        delta = ALTP_DELTA( pBNS->alt_path );
        vLast = ALTP_END_ATOM( pBNS->alt_path );
        v2 = NO_VERTEX;
        pAATG->nNumFound = 0; /* initialize */

        if (nEnd1 != vFirst && nEnd1 != vLast)
        {
            nEnd1 = -1; /* really not the end */
        }
        if (nEnd2 != vFirst && nEnd2 != vLast)
        {
            nEnd2 = -1; /* really not the end */
        }

        for (i = 0; i < n; i++, delta = -delta, v1 = v2)
        {
            ineigh1 = ALTP_THIS_ATOM_NEIGHBOR( pBNS->alt_path, i );  /* v1->v2 neighbor */
            ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);  /* v2->v1 neighbor */ /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
            edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1];
            /* follow the BN Structure, not the inp_ATOM, to take care of swithching to
            t-groups, c-groups or other fictitious edges/vertices
            */
            v2 = edge->neighbor12 ^ v1;
            /*
            if ( v1 < num_atoms && v2 < num_atoms ) {
            continue;
            }
            */
            /* BNS increased edge flow by delta */
            if (v1 >= num_atoms &&
                ( ( pBNS->vert[v1].type & BNS_VERT_TYPE_TGROUP ) || ( pBNS->vert[v1].type & BNS_VERT_TYPE_TEMP ) ) &&
                 0 <= v2 && v2 < num_atoms && ( pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM ))
            {
                /*
                if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) {
                pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH;
                pAATG->nNumFound ++;
                }
                */

                /* BNS increased bond order in  v1(t-group)-v2(atom) by delta: added delta attachments */
                if (nLenDelta < MAX_ALT_AATG_ARRAY_LEN)
                {
                    cDelta[nLenDelta] = delta;
                    nVertex[nLenDelta] = v2;
                    nLenDelta++;
                }
            }
            else
            {
                if (v2 >= num_atoms &&
                    ( ( pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP ) || ( pBNS->vert[v2].type & BNS_VERT_TYPE_TEMP ) ) &&
                     0 <= v1 && v1 < num_atoms && ( pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM ))
                {
                    /*
                    if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) {
                    pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH;
                    pAATG->nNumFound ++;
                    }
                    */

                    /* BNS increased bond order in  v1(atom)-v2(t-group) by delta: added delta attachments */
                    if (nLenDelta < MAX_ALT_AATG_ARRAY_LEN)
                    {
                        cDelta[nLenDelta] = delta;
                        nVertex[nLenDelta] = v1;
                        nLenDelta++;
                    }
                }
                else
                {
                    /* Special case when the testing 'dot' was placed on an atom (should be nEnd1 only) */
                    if ((0 <= v1 && v1 == nEnd1) || (v1 == nEnd2 && 0 <= v2 && v2 < num_atoms)) /* djb-rwth: addressing LLVM warning */
                    {
                        if (nLenDelta < MAX_ALT_AATG_ARRAY_LEN)
                        {
                            cDelta[nLenDelta] = -delta;
                            nVertex[nLenDelta] = v1;
                            nLenDelta++;
                        }
                    }
                    else
                    {
                        if ((0 <= v2 && v2 == nEnd1) || (v2 == nEnd2 && 0 <= v1 && v1 < num_atoms)) /* djb-rwth: addressing LLVM warning */
                        {
                            if (nLenDelta < MAX_ALT_AATG_ARRAY_LEN)
                            {
                                cDelta[nLenDelta] = -delta;
                                nVertex[nLenDelta] = v2;
                                nLenDelta++;
                            }
                        }
                    }
                }
            }
        }

        if (v2 != vLast)
        {
            err = BNS_PROGRAM_ERR;
        }
        else
        {
            last_i = -1;
            nNumFound = 0;
            /* first run */
            for (i = 1, j = 0; i < nLenDelta; j = i++)
            {
                /* Ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they  */
                /* describe ordinary aug. paths of moving a single attachment     */
                /* we are looking for aug. paths describing movement of 2 or more */
                if ((cDelta[j] > 0 && cDelta[i] > 0) ||
                     (cDelta[j] < 0 && cDelta[i] < 0)) /* djb-rwth: addressing LLVM warning */
                {
                    if (j == last_i)
                    {
                        /* Three attachments moved */
                        return 0;
                    }
                    v1 = nVertex[j];
                    if (!( pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH ))
                    {
                        nNumFound++;
                    }
                    v2 = nVertex[i];
                    if (!( pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH ))
                    {
                        nNumFound++;
                    }
                    last_i = i;
                }
            }
            if (!nNumFound)
            {
                return 0;
            }
            if (nNumFound > 4)
            {
                return 0;
            }
            if (nNumFound < 4)
            {
                return 0;
            }

            /* Second run */
            for (i = 1, j = 0; i < nLenDelta; j = i++)
            {
                /* Ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they  */
                /* describe ordinary aug. paths of moving a single attachment     */
                /* we are looking for aug. paths describing movement of 2 or more */
                if ((cDelta[j] > 0 && cDelta[i] > 0) ||
                     (cDelta[j] < 0 && cDelta[i] < 0)) /* djb-rwth: addressing LLVM warning */
                {
                    v1 = nVertex[i - 1];
                    if (!( pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH ))
                    {
                        pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH;
                        pAATG->nNumFound++;
                    }
                    v2 = nVertex[i];
                    if (!( pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH ))
                    {
                        pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH;
                        pAATG->nNumFound++;
                    }
                }
            }
        }
    }

    return err ? err : pAATG->nNumFound;
}


/****************************************************************************/
int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow )
{
    int       pass, i, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, err = 0; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
    BNS_EDGE *edge;

    for (pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass--)
    {
        pBNS->alt_path = pBNS->altp[pass];
        v1 = ALTP_START_ATOM( pBNS->alt_path );
        n = ALTP_PATH_LEN( pBNS->alt_path );
        delta = ALTP_DELTA( pBNS->alt_path );
        vLast = ALTP_END_ATOM( pBNS->alt_path );
        v2 = NO_VERTEX;
        /* starting vertex */
        if (( bChangeFlow & BNS_EF_CHNG_RSTR ) == BNS_EF_CHNG_RSTR)
        {
            pBNS->vert[v1].st_edge.flow -= delta; /* restore s-v1 edge flow to the BNS input value */
        }
        else
        {
            if (( bChangeFlow & BNS_EF_SAVE_ALL ) == BNS_EF_SAVE_ALL)
            {
                pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow;
            }
        }

        /* Augmenting path edges */
        for (i = 0; i < n; i++, delta = -delta, v1 = v2)
        {
            ineigh1 = ALTP_THIS_ATOM_NEIGHBOR( pBNS->alt_path, i );  /* v1->v2 neighbor */
            ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);  /* v2->v1 neighbor */ /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
            edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1];
            v2 = edge->neighbor12 ^ v1;
            RestoreEdgeFlow( edge, delta, bChangeFlow );
            edge->pass = 0;
        }

        /* Ending vertex */
        if (v2 != vLast)
        {
            err = BNS_PROGRAM_ERR;
        }
        else
        {
            if (( bChangeFlow & BNS_EF_CHNG_RSTR ) == BNS_EF_CHNG_RSTR)
            {
                /* Restore v2-t edge flow to the original value */
                /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */
                pBNS->vert[v2].st_edge.flow += delta;
            }
            else
            {
                if (( bChangeFlow & BNS_EF_SAVE_ALL ) == BNS_EF_SAVE_ALL)
                {
                    pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow;
                }
            }
        }
    }

    return err ? err : ret;
}


/****************************************************************************/
int bNeedToTestTheFlow( int bond_type,
                        int nTestFlow,
                        int bTestForNonStereoBond )
{
    int nBondType = ( BOND_TYPE_MASK & bond_type );
    int nBondAttrib = ( BOND_MARK_MASK & bond_type );

    if (bTestForNonStereoBond)
    {
        if (nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS)
        {
            switch (nTestFlow)
            {
                case 0:  /* single: can be 1 (single)?  */
                    if (nBondAttrib == BOND_MARK_ALT12NS ||
                         nBondAttrib == BOND_MARK_ALT123 ||
                         nBondAttrib == BOND_MARK_ALT13)
                    {
                        return 0; /* yes, already checked */
                    }
                    break;

                case 1:  /* double: can be 2 (double)? */
                    if (nBondAttrib == BOND_MARK_ALT12NS ||
                         nBondAttrib == BOND_MARK_ALT123 ||
                         nBondAttrib == BOND_MARK_ALT23)
                    {
                        return 0; /* yes, already checked */
                    }
                    break;
                case 2:  /* triple: can be 3 (triple)?  */
                    if (nBondAttrib == BOND_MARK_ALT13 ||
                         nBondAttrib == BOND_MARK_ALT123 ||
                         nBondAttrib == BOND_MARK_ALT23)
                    {
                        return 0; /* yes, already checked */
                    }
                    break;
            }
        }
    }
    else
    {
        if (nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS)
        {
            switch (nTestFlow)
            {
                case 0:  /* single: can be 1 (single)?  */
                    if (nBondAttrib == BOND_MARK_ALT12 ||
                         nBondAttrib == BOND_MARK_ALT12NS ||
                         nBondAttrib == BOND_MARK_ALT123 ||
                         nBondAttrib == BOND_MARK_ALT13)
                    {
                        return 0;
                    }
                    break;

                case 1:  /* double: can be 2 (double)? */
                    if (nBondAttrib == BOND_MARK_ALT12 ||
                         nBondAttrib == BOND_MARK_ALT12NS ||
                         nBondAttrib == BOND_MARK_ALT123 ||
                         nBondAttrib == BOND_MARK_ALT23)
                    {
                        return 0; /* yes */
                    }
                    break;
                case 2:  /* triple: can be 3 (triple)?  */
                    if (nBondAttrib == BOND_MARK_ALT13 ||
                         nBondAttrib == BOND_MARK_ALT123 ||
                         nBondAttrib == BOND_MARK_ALT23)
                    {
                        return 0;
                    }
                    break;
            }
        }
    }

    return 1;
}


/****************************************************************************/
int nBondsValenceInpAt( const inp_ATOM *at,
                        int *nNumAltBonds,
                        int *nNumWrongBonds )
{
    int j, bond_type, nBondsValence = 0, nAltBonds = 0, nNumWrong = 0;
    for (j = 0; j < at->valence; j++)
    {
        bond_type = at->bond_type[j] & BOND_TYPE_MASK;
        switch (bond_type)
        {
            case 0:  /* for structure from InChI reconstruction */
            case BOND_SINGLE:
            case BOND_DOUBLE:
            case BOND_TRIPLE:
                nBondsValence += bond_type;
                break;
            case BOND_ALTERN:
                nAltBonds++;
                break;
            default:
                nNumWrong++;
        }
    }
    switch (nAltBonds)
    {
        case 0:
            break;
        case 1:
            nBondsValence += 1; /* 1 or greater than 3 is wrong */
            nNumWrong++;
            break;
        default:
            nBondsValence += nAltBonds + 1;
            break;
    }
    if (nNumAltBonds)
    {
        *nNumAltBonds = nAltBonds;
    }
    if (nNumWrongBonds)
    {
        *nNumWrongBonds = nNumWrong;
    }

    return nBondsValence;
}


/****************************************************************************
If radical or has aromatic bonds,
then augment to the lowest "multiplicity"
****************************************************************************/
int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS,
                           BN_DATA *pBD,
                           inp_ATOM *at,
                           int num_atoms )
{
    int  bError = 0, nOrigDelta = 0, ret, num_removed;

#if( CHECK_AROMBOND2ALT == 1 )
    int *pcValMinusBondsVal = NULL;
    int  i, nValMinusBondsVal, nAltBonds, bIgnore, valen, is_rad, excess;

    /* Find valence excess (it may only be due to aromatic bonds) */
    for (i = 0; i < num_atoms; i++)
    {
        valen = nBondsValenceInpAt( at + i, &nAltBonds, &bIgnore );
        nValMinusBondsVal = (int) at[i].chem_bonds_valence - valen;
        bIgnore += ( nAltBonds > 3 );
        if (!bIgnore && nValMinusBondsVal > 0)
        {
            if (!pcValMinusBondsVal &&
                 !( pcValMinusBondsVal = (int *) inchi_calloc( num_atoms, sizeof( pcValMinusBondsVal[0] ) ) ))
            {
                bError = BNS_OUT_OF_RAM;
                goto exit_function;
            }
            /* Mark atoms that have extra unsatisfied valence due to aromatic bonds */
            is_rad = ( at[i].radical == RADICAL_DOUBLET );
            excess = nValMinusBondsVal + is_rad;
            pcValMinusBondsVal[i] = excess;
        }
    }
#endif /* CHECK_AROMBOND2ALT */

    /* Match bonds to valences */
    do
    {
        num_removed = 0;
        ret = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW );
        if (IS_BNS_ERROR( ret ))
        {
            bError = ret;
        }
        else
        {
            nOrigDelta += ret;
            num_removed = pBNS->num_altp; /* number of augmenting paths */
            if (ret > 0)
            {
                /* save new bonds in at[] and flows in pBNS and at[] */
                ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */
                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                }
                ret = RestoreBnStructFlow( pBNS, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */
                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                }
            }
            ReInitBnStructAltPaths( pBNS );
        }
    } while (num_removed && num_removed == pBNS->max_altp && !bError);

#if( CHECK_AROMBOND2ALT == 1 )
    /* Check whether aromatic bonds have been replaced with alternating bonds */
    if (!bError && pcValMinusBondsVal)
    {
        for (i = 0; i < num_atoms; i++)
        {
            if (!pcValMinusBondsVal[i])
            {
                continue;
            }
            valen = nBondsValenceInpAt( at + i, &nAltBonds, &bIgnore );
            nValMinusBondsVal = (int) at[i].chem_bonds_valence - valen;
            is_rad = ( at[i].radical == RADICAL_DOUBLET );
            excess = nValMinusBondsVal + is_rad;
            if (bIgnore ||
                ( pcValMinusBondsVal[i] - excess ) != 1)
            {
                /* radical excess has not been reduced */
                bError = BNS_ALTBOND_ERR;
                break;
            }
        }
    }

exit_function:
    if (pcValMinusBondsVal)
    {
        inchi_free( pcValMinusBondsVal );
    }
#endif /* CHECK_AROMBOND2ALT */

    return bError ? bError : nOrigDelta;
}


/****************************************************************************/
int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS,
                            BN_DATA *pBD,
                            inp_ATOM *at,
                            int num_atoms,
                            BNS_FLOW_CHANGES *fcd,
                            int bChangeFlow,
                            int nBondTypeToTest )
{
    int ret, iat, ineigh, neigh;
    int nMinFlow, nMaxFlow, nTestFlow, nCurFlow;
    int iedge, bError, nDots, nChanges, bTestForNonStereoBond; /* djb-rwth: removing redundant variables */

    /* Normalize bonds and find tautomeric groups */
    bError = 0;
    nChanges = 0;
    bTestForNonStereoBond = pBNS->tot_st_cap > pBNS->tot_st_flow;

    for (iat = 0; iat < num_atoms && !bError; iat++)
    {
        for (ineigh = 0; ineigh < at[iat].valence && !bError; ineigh++)
        {
            neigh = at[iat].neighbor[ineigh];
            if (neigh < iat)
            {
                continue; /* we have already tested the bond */
            }
            iedge = pBNS->vert[iat].iedge[ineigh];
            if (IS_FORBIDDEN( pBNS->edge[iedge].forbidden, pBNS ))
            {
                continue;
            }
            if (nBondTypeToTest && ( at[iat].bond_type[ineigh] & BOND_TYPE_MASK ) != nBondTypeToTest)
            {
                continue;
            }
            nMinFlow = nMinFlow2Check( pBNS, iedge );
            nMaxFlow = nMaxFlow2Check( pBNS, iedge );
            nCurFlow = nCurFlow2Check( pBNS, iedge );
            if (nMinFlow == nMaxFlow)
            {
                if (nMaxFlow && bTestForNonStereoBond)
                {
                    nTestFlow = nMaxFlow - (int) ( pBNS->tot_st_cap - pBNS->tot_st_flow ); /* temporary use of nTestFlow */
                    nMinFlow = inchi_max( 0, nTestFlow );
                }
                else
                {
                    continue;
                }
            }
            for (nTestFlow = nMinFlow; nTestFlow <= nMaxFlow && !bError; nTestFlow++)
            {
                if (nTestFlow == nCurFlow)
                {
                    continue;
                }
                if (!bNeedToTestTheFlow( at[iat].bond_type[ineigh], nTestFlow, bTestForNonStereoBond ))
                {
                    continue;
                }
                /* djb-rwth: removing redundant code */
                nDots = bSetFlowToCheckOneBond( pBNS, iedge, nTestFlow, fcd );

                if (IS_BNS_ERROR( nDots ))
                {
                    if (nDots == BNS_CANT_SET_BOND)
                    {
                        ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd );
                        if (!IS_BNS_ERROR( ret ))
                        {
                            continue;
                        }
                    }
                    bError = nDots;
                }
                else
                {
                    if (nDots > 0)
                    {
                        ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow );
                        if (IS_BNS_ERROR( ret ))
                        {
                            bError = ret;
                        }
                        else
                        {
                            if (ret > 0)
                            {
                                if (2 * ret == nDots)
                                {
                                    ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow );
                                    if (IS_BNS_ERROR( ret ))
                                    {
                                        bError = ret;
                                    }
                                    else
                                    {
                                        nChanges += ( ret & 1 );
                                        ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow );
                                        if (IS_BNS_ERROR( ret ))
                                        {
                                            bError = ret;
                                        }
                                        else
                                        {
                                            if (ret >= 0)
                                            {
                                                nChanges += ( ret & 1 );
                                                /* djb-rwth: removing redundant code */
                                            }
                                            else
                                            {
                                                bError = ret;
                                            }
                                        }
                                    }
                                }
                                /* typically 2*ret < nDots; 2*ret > nDots should not happen. Check later */
                                ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR );
                                if (IS_BNS_ERROR( ret ))
                                {
                                    bError = ret;
                                }
                            }
                        }
                        /* --- Reinitialize to repeat the calculations --- */
                        ReInitBnStructAltPaths( pBNS );
                    }

                    else
                    {
                        if (nDots == 0)
                        {
                            ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow );
                            if (IS_BNS_ERROR( ret ))
                            {
                                bError = ret;
                            }
                            else
                            {
                                nChanges += ( ret & 1 );
                            }
                        }
                    }
                }

                ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd );

                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                }

            } /* for (nTestFlow = */
        } /* for (ineigh = */
    } /* for (iat =  */

    return bError ? bError : nChanges;
}


/****************************************************************************/
static void remove_alt_bond_marks( inp_ATOM *at, int num_atoms )
{
    int i, j, val;
    for (i = 0; i < num_atoms; i++)
    {
        for (val = at[i].valence, j = 0; j < val; j++)
        {
            at[i].bond_type[j] &= BOND_TYPE_MASK;
        }
    }

}


/****************************************************************************/
int SetForbiddenEdges( BN_STRUCT *pBNS,
                       inp_ATOM *at,
                       int num_atoms,
                       int forbidden_mask,
                       int nebend,
                       int *ebend )
{


    int i, j, neigh, num_found;
    BNS_IEDGE iedge;
    /*S_CHAR    edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/
    S_CHAR    edge_forbidden_mask = forbidden_mask;

    pBNS->edge_forbidden_mask |= forbidden_mask;

    num_found = 0;

    for (i = 0; i < num_atoms; i++)
    {
        /* acetyl */
        if (at[i].el_number == EL_NUMBER_C && 3 == at[i].valence &&
             4 == at[i].chem_bonds_valence)
        {
            int num_O = 0;
            int bond_to_O_val = 0;
            int forbidden_bond_pos = -1;
            int forbidden_bond_val = -1;
            for (j = 0; j < at[i].valence; j++)
            {
                neigh = at[i].neighbor[j];
                if (at[neigh].el_number == EL_NUMBER_O &&
                     at[neigh].valence == 1)
                {
                    num_O++;
                    bond_to_O_val += ( at[i].bond_type[j] & BOND_TYPE_MASK );
                }
                else
                {
                    forbidden_bond_pos = j;
                    forbidden_bond_val = ( at[i].bond_type[j] & BOND_TYPE_MASK );
                }
            }
            if (2 == num_O && 3 == bond_to_O_val && 1 == forbidden_bond_val)
            {
                iedge = pBNS->vert[i].iedge[forbidden_bond_pos];
                pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                num_found++;
            }
        }
        else
        {
            /* nitro */
            if (at[i].el_number == EL_NUMBER_N && 3 == at[i].valence &&
                ( 4 == at[i].chem_bonds_valence || 5 == at[i].chem_bonds_valence ))
            {
                int num_O = 0;
                int bond_to_O_val = 0;
                int forbidden_bond_pos = -1;
                int forbidden_bond_val = -1;
                for (j = 0; j < at[i].valence; j++)
                {
                    neigh = at[i].neighbor[j];
                    if (at[neigh].el_number == EL_NUMBER_O &&
                         at[neigh].valence == 1)
                    {
                        num_O++;
                        bond_to_O_val += ( at[i].bond_type[j] & BOND_TYPE_MASK );
                    }
                    else
                    {
                        forbidden_bond_pos = j;
                        forbidden_bond_val = ( at[i].bond_type[j] & BOND_TYPE_MASK );
                    }
                }
                if (2 == num_O && ( 3 == bond_to_O_val || 4 == bond_to_O_val ) && 1 == forbidden_bond_val)
                {
                    iedge = pBNS->vert[i].iedge[forbidden_bond_pos];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_found++;
                }
            }
        }
    }

#ifdef FIX_SRU_CYCLIZING_PS_BONDS_IN_BNS
    if (nebend > 1)
        num_found += fix_explicitly_indicated_bonds( nebend, ebend, pBNS, at, num_atoms );
#endif

#if ( REMOVE_ION_PAIRS_FIX_BONDS == 1 )
    num_found += fix_special_bonds( pBNS, at, num_atoms, edge_forbidden_mask );
#endif
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
    num_found += TempFix_NH_NH_Bonds( pBNS, at, num_atoms );
#endif

    return num_found;
}



#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )


/****************************************************************************/
int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
{
    int i, j, neigh, num_found;
    BNS_IEDGE iedge;
    S_CHAR    edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP;
    for (i = 0, num_found = 0; i < num_atoms; i++)
    {
        /* -NH-NH- or -NH-NH3 */
        if (at[i].el_number == EL_NUMBER_N && at[i].valence < 3 && at[i].num_H &&
             3 == at[i].chem_bonds_valence + at[i].num_H   &&
             at[i].chem_bonds_valence == at[i].valence &&
             !at[i].charge && !at[i].radical)
        {
            for (j = 0; j < at[i].valence; j++)
            {
                neigh = at[i].neighbor[j];
                if (neigh < i &&
                     at[neigh].el_number == EL_NUMBER_N && at[neigh].valence < 3 && at[neigh].num_H &&
                     3 == at[neigh].chem_bonds_valence + at[neigh].num_H   &&
                     at[neigh].chem_bonds_valence == at[neigh].valence &&
                     !at[neigh].charge && !at[neigh].radical)
                {
                    iedge = pBNS->vert[i].iedge[j];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_found++;
                }
            }
        }
    }

    return num_found;
}


/****************************************************************************/
int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
{
    int i, j, neigh, num_found;
    BNS_IEDGE iedge;
    S_CHAR    edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP;

    for (i = 0, num_found = 0; i < num_atoms; i++)
    {
        /* -NH-NH- or -NH-NH3 */
        if (at[i].el_number == EL_NUMBER_N && at[i].valence < 3)
        {
            for (j = 0; j < at[i].valence; j++)
            {
                neigh = at[i].neighbor[j];
                if (neigh < i &&
                     at[neigh].el_number == EL_NUMBER_N && at[neigh].valence < 3)
                {
                    if (BOND_TYPE_SINGLE != ( at[i].bond_type[j] & BOND_TYPE_MASK ))
                    {
                        iedge = pBNS->vert[i].iedge[j];
                        if (pBNS->edge[iedge].forbidden & edge_forbidden_mask)
                        {
                            pBNS->edge[iedge].forbidden &= ~edge_forbidden_mask;
                            num_found++;
                        }
                    }
                }
            }
        }
    }

    return num_found;
}
#endif

/****************************************************************************
Fixes bonds which were set by remove_ion_pairs( ... )
****************************************************************************/
int fix_special_bonds( BN_STRUCT *pBNS,
                       inp_ATOM *at,
                       int num_atoms,
                       int forbidden_mask )
{
    int num_changes = 0;

#define MAX_NEIGH 6

    int i, k, n1, n2, n3, i1, i2, i3, i4, bond_type; /* djb-rwth: removing redundant variables */
    inp_ATOM *a;
    int j[3], m[3], num_O, k_O, num_N, num_OH, num_OM, num_X, num_other, k_N;

    BNS_IEDGE iedge;
    /*S_CHAR    edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/
    S_CHAR    edge_forbidden_mask = forbidden_mask;

    pBNS->edge_forbidden_mask |= edge_forbidden_mask;

    for (i = 0, a = at; i < num_atoms; i++, a++)
    {

        if (!a->charge && !a->radical &&
             2 <= a->chem_bonds_valence + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
             0 == num_of_H( at, i ) &&
             2 == nNoMetalBondsValence( at, i ) + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
             ion_el_group( at[i].el_number ) == EL_NUMBER_N )
        {
            /* Found N(V), no H */
            if (2 == nNoMetalNumBonds( at, i ))
            {
                /* #N= */
                /* fix bonds: double and triple: =N# so that bonds cannot be changed by the normalization */
#if ( FIX_N_V_METAL_BONDS_GPF == 1 )
                if (0 > ( i1 = nNoMetalNeighIndex( at, i ) ) ||
                     0 > ( i2 = nNoMetalOtherNeighIndex( at, i, n1 = a->neighbor[i1]/* non-metal neighbor #1 */ ) ))
                {
                    /*num_err ++; */ /* do not count would-be original InChI v.1 buffer overflow GPF */
                    continue;       /* v1 bug: 2 bonds to metal yield i1 < 0 and/or i2 < 0 => bounds violation */
                }
#else
                i1 = nNoMetalNeighIndex( at, i );
                n1 = a->neighbor[i1]; /* non-metal neighbor #1 */
                i2 = nNoMetalOtherNeighIndex( at, i, n1 );
#endif
                n2 = a->neighbor[i2]; /* non-metal neighbor #2 */
                                      /* forbid all edges to non-metals */
                iedge = pBNS->vert[i].iedge[i1];
                pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */
                iedge = pBNS->vert[i].iedge[i2];
                pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */
                num_changes++;  /* added 11-15-2005 */
                                /*                                                      i n3 */
                                /* forbid single bond edge beyond the neighboring =N- as in #N=N- */
                /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                if (( at[i].bond_type[i1] & BOND_TYPE_MASK ) == BOND_TYPE_DOUBLE)
                {
                    i3 = i1; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                    n3 = n1;
                }
                else
                {
                    i3 = i2; /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                    n3 = n2;
                }
                if (0 == NUMH( at, n3 ) && 2 == nNoMetalNumBonds( at, n3 ) &&
                     3 == nNoMetalBondsValence( at, n3 ) &&
                     ion_el_group( at[n3].el_number ) == EL_NUMBER_N &&
                     0 <= ( k = nNoMetalOtherNeighIndex( at, n3, i ) ))
                {
                    /* found =N- ; forbid the edge*/
                    iedge = pBNS->vert[n3].iedge[k];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                }
            }
            else if (3 == nNoMetalNumBonds( at, i ) &&
                      /*         |                      */
                      /* found  =N=                     */
                      /* locate all non-metal neighbors */
                      0 <= ( j[0] = nNoMetalNeighIndex( at, i ) ) &&
                      0 <= ( j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = a->neighbor[j[0]] ) ) &&
                      0 <= ( j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = a->neighbor[j[1]] ) ))
            {
                /* Count specific neighbors: N(V)=N, N(V)-N N(V)=O, and N(V)-N */

                /*  if there is a single neighbor connected by a double bond, namely
                N(V)=N and/or N(V)=O, then fix the bond(s).
                If N(V)=O was fixed then do not fix another bond */
                m[2] = a->neighbor[j[2]];
                num_O = num_N = 0;
                for (k = 0; k < 3; k++)
                {
                    n1 = m[k];
                    i1 = j[k]; /* djb-rwth: ignoring LLVM warning: variable used */
                    if (ion_el_group( at[n1].el_number ) == EL_NUMBER_N)
                    {
                        k_N = k;
                        num_N++;
                    }
                    else if (ion_el_group( at[n1].el_number ) == EL_NUMBER_O &&
                             1 == nNoMetalNumBonds( at, n1 ))
                    {
                        k_O = k;
                        num_O++;
                    }
                }
                num_other = 0;
                if (1 == num_O &&
                     0 == at[n1 = m[k_O]].charge &&
                     0 == at[n1].radical            &&
                     BOND_TYPE_DOUBLE == ( at[i].bond_type[i1 = j[k_O]] & BOND_TYPE_MASK )
                     )
                {
                    /* Fix bond to neighbor =O */
                    iedge = pBNS->vert[i].iedge[i1];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                    num_other++; /* indicator: double to a terminal O has been fixed */
                }
                if (!num_other                    &&
                     num_O <= 1 &&
                     1 == num_N &&
                     0 == at[n1 = m[k_N]].charge &&
                     0 == at[n1].radical            &&
                     BOND_TYPE_DOUBLE == ( at[i].bond_type[i1 = j[k_N]] & BOND_TYPE_MASK ))
                {
                    /* Fix bond to neighbor =N */
                    iedge = pBNS->vert[i].iedge[i1];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                }
            }
            else if (4 == nNoMetalNumBonds( at, i ))
            {
                /*         |                      */
                /* found  -N=N-                   */
                /*         |                      */
                /* locate non-metal neighbor connected by a double bond;
                * if it is =N- then fix the double bond and the single bond beyond the neighbor
                */
                num_N = 0;
                num_other = 0; /* djb-rwth: ignoring LLVM warning: variable used */
                for (i1 = 0; i1 < at[i].valence; i1++)
                {
                    if (BOND_TYPE_DOUBLE == ( at[i].bond_type[i1] & BOND_TYPE_MASK ) &&
                         !is_el_a_metal( at[n1 = (int) at[i].neighbor[i1]].el_number ) &&
                         ion_el_group( at[n1].el_number ) == EL_NUMBER_N)
                    {
                        num_N++;
                        n2 = n1;
                        i2 = i1;
                    }
                }
                if (1 == num_N && 0 == NUMH( at, n2 ) &&
                     2 == nNoMetalNumBonds( at, n2 ) &&
                     3 == nNoMetalBondsValence( at, n2 ) &&
                     0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) &&
                     BOND_TYPE_SINGLE == ( at[n2].bond_type[i3] & BOND_TYPE_MASK ))
                {
                    /* fix the single bond beyond the N(V) neighbor N(V)=N- */
                    iedge = pBNS->vert[n2].iedge[i3];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                    /* fix the double bond */
                    iedge = pBNS->vert[i].iedge[i2];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                }
            }
        }

        else if (!a->charge && !a->radical &&
                  2 <= a->chem_bonds_valence + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
                  0 == num_of_H( at, i ) &&
                  2 == nNoMetalBondsValence( at, i ) + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
                  a->el_number != EL_NUMBER_O && ion_el_group( a->el_number ) == EL_NUMBER_O &&
                  3 == nNoMetalNumBonds( at, i ))
        {
            /* Found S(IV), no H, one double bond, total 3 bonds */
            /*       OH
            /
            in O=S   (X != O) fix single bond O-X  (type 1)
            \
            X

            X
            /
            in Z=S    (X, Y != OH) fix double bond Z=S (type 2)
            \
            Y
            */
            num_N = 0;  /* number of non-metal neighbors connected by a double bond */
            num_OH = 0; /* number of neighbors OH connected by a single bond */
            num_OM = 0; /* number of charged neighbors O connected by a single bond */
            num_O = 0;  /* number of neighbors =O connected by a double bond */
            num_other = 0;

            for (i1 = 0; i1 < a->valence; i1++)
            {
                n1 = (int) a->neighbor[i1];
                if (is_el_a_metal( at[n1].el_number ))
                {
                    continue;
                }

                bond_type = ( a->bond_type[i1] & BOND_TYPE_MASK );

                if (BOND_TYPE_DOUBLE == bond_type)
                {
                    num_N++;
                    n2 = n1;
                    i2 = i1;
                    if (ion_el_group( at[n1].el_number ) == EL_NUMBER_O)
                    {
                        num_O++;
                    }
                }
                else if (BOND_TYPE_SINGLE == bond_type &&
                          1 == nNoMetalNumBonds( at, n1 ) &&
                          1 == nNoMetalBondsValence( at, n1 ) &&
                          ion_el_group( at[n1].el_number ) == EL_NUMBER_O)
                {
                    if (0 == at[n1].charge)
                    {
                        num_OH++;
                        n3 = n1;
                        i3 = i1; /* djb-rwth: ignoring LLVM warning: variable used */
                    }
                    else
                    {
                        num_OM++;
                    }
                }
                else
                {
                    num_other++;
                    /* djb-rwth: removing redundant code */
                    i4 = i1;
                }
            } /* for (i1 = */

            if (1 == num_N && 1 == num_O && 1 == num_OH + num_OM)
            {
                if (1 == num_other)
                {
                    /* type 1: fix the single bond S-X */
                    iedge = pBNS->vert[i].iedge[i4];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                }
            }
            else if (1 == num_N && !num_OH && !num_OM)
            {
                int    bFound = 0; /* flag */
                int    bDoNotFixAnyBond = 0; /* flag */
                                             /* Avoid case N=S-NH or N=S-N(-); N = N, P, As, Sb */
                if (ion_el_group( at[n2].el_number ) == EL_NUMBER_N)
                {
                    U_CHAR el_number = at[n2].el_number;
                    for (i1 = 0; i1 < a->valence; i1++)
                    {
                        n1 = (int) a->neighbor[i1];
                        bond_type = ( a->bond_type[i1] & BOND_TYPE_MASK );
                        if (BOND_TYPE_SINGLE == bond_type &&
                            ( NUMH( at, n1 ) || -1 == at[n1].charge ) &&
                             el_number == at[n1].el_number)
                        {
                            i3 = i1; /* djb-rwth: ignoring LLVM warning: variable used */
                            n3 = n1;
                            bFound++;
                        }
                    }
                }
                /* Exception: check if Z==X and they belong to the same ring system */
                for (i1 = 0; i1 < a->valence; i1++)
                {
                    if (i1 != i2)
                    {
                        n1 = (int) a->neighbor[i1];
                        if (at[n2].el_number == at[n1].el_number &&
                             at[n2].nRingSystem == at[n1].nRingSystem)
                        {
                            bDoNotFixAnyBond++;
                        }
                    }
                }
                if (bDoNotFixAnyBond)
                {
                    ; /* do nothing */
                }
                else if (bFound)
                {
                    if (1 == bFound &&
                         0 <= ( i4 = nNoMetalOtherNeighIndex2( at, i, n2, n3 ) ))
                    {
                        /* fix bond i4 */
                        iedge = pBNS->vert[i].iedge[i4];
                        pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                        num_changes++;
                    }
                }
                else
                {
                    /* fix the double bond >S=X */
                    iedge = pBNS->vert[i].iedge[i2];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                    /* -- test later --
                    if ( 2 == nNoMetalNumBonds( at, n2 ) &&
                    0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) ) {
                    iedge = pBNS->vert[n2].iedge[i3];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes ++;
                    }
                    -------------------*/
                }
            }
        }

        else if (!a->charge && !a->radical &&
                  4 <= a->chem_bonds_valence + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
                  0 == num_of_H( at, i ) &&
                  4 == nNoMetalBondsValence( at, i ) + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
                  a->el_number != EL_NUMBER_O && ion_el_group( a->el_number ) == EL_NUMBER_O &&
                  4 == nNoMetalNumBonds( at, i ))
        {
            /* Found S(VI), no H, two double bonds or one triple bond */
            /*     O
            ||
            in O=S--Y-   (X, Y -- non-terminal) fix single bonds S-X, S-Y  (type 1)
            \
            X--

            O
            ||
            in O=S--O(-)    (X -- non-terminal) fix single bond S-X (type 2)
            \
            X--

            O
            ||
            in O=S--OH     (X -- non-terminal) fix single bond S-X (type 3)
            \
            X--

            */
            int iN[4];  /* indexes of non-terminal neighbors connected by a single bond */
            num_N = 0; /* number of non-metal neighbors connected by a double bond */
            num_OH = 0; /* number of neighbors OH connected by a single bond */
            num_OM = 0; /* number of non-terminal neighbors connected by a single bond */
            num_O = 0; /* number of neighbors =O connected by a double bond */
            num_X = 0; /* number of terminal atom X != O connected by a single bond */
            num_other = 0;
            for (i1 = 0; i1 < a->valence; i1++)
            {
                n1 = (int) a->neighbor[i1];
                if (is_el_a_metal( at[n1].el_number ))
                {
                    continue;
                }
                bond_type = ( a->bond_type[i1] & BOND_TYPE_MASK );
                if (BOND_TYPE_DOUBLE == bond_type)
                {
                    num_N++;
                    if (( 0 == at[n1].charge
#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 )
                          || (1 == at[n1].charge && 2 == at[n1].valence) /* djb-rwth: addressing LLVM warning */
#endif
                          ) &&
                         0 == at[n1].radical &&
                         0 == num_of_H( at, n1 ) &&
                         ion_el_group( at[n1].el_number ) == EL_NUMBER_O &&
                         1 == nNoMetalNumBonds( at, n1 ))
                    {
                        num_O++;
                    }
                }
                else if (BOND_TYPE_SINGLE == bond_type &&
                          1 == nNoMetalNumBonds( at, n1 ) &&
                          ion_el_group( at[n1].el_number ) == EL_NUMBER_O &&
                          1 >= num_of_H( at, n1 ) &&
                          1 == ( ( 0 == at[n1].charge ) && 1 == num_of_H( at, n1 ) )
                          + ( ( -1 == at[n1].charge ) && 0 == num_of_H( at, n1 ) ))
                {
                    num_OH++; /* -OH or -O(-) */
                }
                else if (BOND_TYPE_SINGLE == bond_type &&
                          1 < nNoMetalNumBonds( at, n1 ))
                {
                    iN[num_OM++] = i1; /* non-terminal neighbor connected by a single bond */
                }
                else if (BOND_TYPE_SINGLE == bond_type &&
                          1 == nNoMetalNumBonds( at, n1 ))
                {
                    num_X++; /* other terminal neighbor connected by a single bond */
                }
                else
                {
                    num_other++;
                }
            }
            if (num_N == num_O && 2 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other)
            {
                for (i2 = 0; i2 < num_OM; i2++)
                {
                    i1 = iN[i2];
                    /* fix bond i1 */
                    iedge = pBNS->vert[i].iedge[i1];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                }
            }
        }

        else if (!a->charge && !a->radical &&
                  6 <= a->chem_bonds_valence + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
                  0 == num_of_H( at, i ) &&
                  6 == nNoMetalBondsValence( at, i ) + NUMH( a, 0 ) - get_el_valence( a->el_number, 0, 0 ) &&
                  a->el_number != EL_NUMBER_O && ion_el_group( a->el_number ) == EL_NUMBER_O &&
                  5 == nNoMetalNumBonds( at, i ))
        {
            /* Found S(VIII), no H, three double bonds or two triple bond */
            /*

            O
            ||
            in O=S--Y--     (X, Y -- non-terminal) fix single bond S-X, S-Y (type 4)
            //\
            O   X--

            note: this structure is a mistakenly drawn structure

            O                  O
            ||                 ||
            O=S--O--Y--  or    O=S--Y--
            \                  \
            X--                O--X--


            */
            int iN[5];  /* indexes of non-terminal neighbors connected by a single bond */
            num_N = 0; /* number of non-metal neighbors connected by a double bond */
            num_OH = 0; /* number of neighbors OH connected by a single bond */
            num_OM = 0; /* number of non-terminal neighbors connected by a single bond */
            num_O = 0; /* number of neighbors =O connected by a double bond */
            num_X = 0; /* number of terminal atom X != O connected by a single bond */
            num_other = 0;
            for (i1 = 0; i1 < a->valence; i1++)
            {
                n1 = (int) a->neighbor[i1];
                if (is_el_a_metal( at[n1].el_number ))
                {
                    continue;
                }
                bond_type = ( a->bond_type[i1] & BOND_TYPE_MASK );
                if (BOND_TYPE_DOUBLE == bond_type)
                {
                    num_N++;
                    if (( 0 == at[n1].charge
#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 )
                          || (1 == at[n1].charge && 2 == at[n1].valence) /* djb-rwth: addressing LLVM warning */
#endif
                          )
                         && 0 == at[n1].radical &&
                         0 == num_of_H( at, n1 ) &&
                         ion_el_group( at[n1].el_number ) == EL_NUMBER_O &&
                         1 == nNoMetalNumBonds( at, n1 ))
                    {
                        num_O++;
                    }
                }
                else if (BOND_TYPE_SINGLE == bond_type &&
                          1 == nNoMetalNumBonds( at, n1 ) &&
                          ion_el_group( at[n1].el_number ) == EL_NUMBER_O &&
                          1 >= num_of_H( at, n1 ) &&
                          1 == ( ( 0 == at[n1].charge ) && 1 == num_of_H( at, n1 ) )
                          + ( ( -1 == at[n1].charge ) && 0 == num_of_H( at, n1 ) ))
                {
                    num_OH++; /* -OH or -O(-) */
                }
                else
                {
                    if (BOND_TYPE_SINGLE == bond_type &&
                         1 < nNoMetalNumBonds( at, n1 ))
                    {

                        iN[num_OM++] = i1; /* non-terminal neighbor connected by a single bond */
                    }
                    else
                    {
                        if (BOND_TYPE_SINGLE == bond_type && 1 == nNoMetalNumBonds( at, n1 ))
                        {

                            num_X++; /* other terminal neighbor connected by a single bond */
                        }
                        else
                        {
                            num_other++;
                        }
                    }
                }
            }
            if (num_N == num_O && 3 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other)
            {
                for (i2 = 0; i2 < num_OM; i2++)
                {
                    i1 = iN[i2];
                    /* fix bond i1 */
                    iedge = pBNS->vert[i].iedge[i1];
                    pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
                    num_changes++;
                }
            }
        }

    } /* for (i =  */

    return num_changes;
}


/****************************************************************************/
int fix_explicitly_indicated_bonds( int nebend,
                                    int *ebend,
                                    BN_STRUCT *pBNS,
                                    inp_ATOM *at,
                                    int num_atoms )
{
    int i, num_changes = 0;
    inp_ATOM *a;
    int j, ia1, ia2, i1 = -1, i2 = -1, index = -1, neigh;
    BNS_IEDGE iedge;
    S_CHAR    edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;
    pBNS->edge_forbidden_mask |= edge_forbidden_mask;

    if (nebend < 1 || !ebend)
    {
        return 0;
    }

    for (j = 0; j < nebend; j += 2)
    {

        ia1 = ebend[2 * j];
        ia2 = ebend[2 * j + 1];

        for (i = 0; i < num_atoms; i++)
        {
            if (at[i].orig_at_number == ia1)
            {
                i1 = i;
            }
            else if (at[i].orig_at_number == ia2)
            {
                i2 = i;
            }
            if (i1 > 0 && i2 > 0)
            {
                break;
            }
        }

        if (i1 < 0 || i2 < 0)
        {
            return 0;
        }

        if (i2 < i1)
        {
            int tmp = i1; i1 = i2; i2 = tmp;
        }

        a = at + i1;
        for (i = 0; i < a->valence; i++)
        {
            neigh = (int) a->neighbor[i];
            if (neigh == i2)
            {
                index = i; break;
            }
        }
        if (index > -1)
        {
            iedge = pBNS->vert[i].iedge[index];
            pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
            num_changes++;
        }
    }

    return num_changes;
}




#define ALL_NONMETAL_Z  0


/****************************************************************************/
int is_Z_atom( U_CHAR el_number )
{
    switch ( el_number ) 
    {
        case EL_NUMBER_C: /* fallthrough */
        case EL_NUMBER_N:
        case EL_NUMBER_P:
        case EL_NUMBER_AS:
        case EL_NUMBER_SB:
        case EL_NUMBER_S:
        case EL_NUMBER_SE:
        case EL_NUMBER_TE:
        case EL_NUMBER_CL:
        case EL_NUMBER_BR:
        case EL_NUMBER_I:
#if ( ALL_NONMETAL_Z == 1 )        
        case EL_NUMBER_B:
        case EL_NUMBER_O:
        case EL_NUMBER_SI:
        case EL_NUMBER_GE:
        case EL_NUMBER_F:
        case EL_NUMBER_AT:
#endif        
            return 1;
        default:
            return 0;    
    }
}


/****************************************************************************
Detect O==Z--X, O=O,S,Se,Te
****************************************************************************/
int IsZOX( inp_ATOM *atom, int at_x, int ord )
{
    inp_ATOM *at_Z = atom + atom[at_x].neighbor[ord];

    int i, neigh, num_O;

    for (i = 0, num_O = 0; i < at_Z->valence; i++)
    {
        neigh = at_Z->neighbor[i];
        if (neigh == at_x)
        {
            continue;
        }
        if (atom[neigh].valence == 1 &&
             atom[neigh].chem_bonds_valence == 2 &&
             atom[neigh].charge == 0 &&
             atom[neigh].radical == 0 &&
             ( atom[neigh].el_number == EL_NUMBER_O ||
               atom[neigh].el_number == EL_NUMBER_S ||
               atom[neigh].el_number == EL_NUMBER_SE ||
               atom[neigh].el_number == EL_NUMBER_TE ))
        {
            num_O++;
        }
    }

    return num_O;
}

#if ( FIX_RENUM_BUG_FOR_CASE_OF_ACIDIC_OH_AT_P_PLUS == 1 )
void update_some_attype_totals(int nAtTypeTotals[], int mask, int delta, S_CHAR at_charge);
/****************************************************************************/
void update_some_attype_totals(int nAtTypeTotals[], int mask, int delta, S_CHAR at_charge)
{
    int i;
    int32_t bit; /* djb-rwth: fixing coverity ID #499534 */
    if (nAtTypeTotals)
    {
        if (mask && !(mask & (ATBIT_Errors)))
        {
            for (i = 0, bit = 1; i < ATTOT_ARRAY_LEN; i++, bit <<= 1)
            {
                if (bit & mask)
                {
                    nAtTypeTotals[i] += delta;
                }
            }
        }
        /* Count charges */
        if (at_charge)
        {
            nAtTypeTotals[ATTOT_TOT_CHARGE] += delta * at_charge;
            nAtTypeTotals[ATTOT_NUM_CHARGES] += delta;
        }
    }
    return;
}
#endif
/****************************************************************************/
int GetAtomChargeType( inp_ATOM *atom,
                       int at_no,
                       int nAtTypeTotals[],
                       int *pMask,
                       int bSubtract )
{

    inp_ATOM *at = atom + at_no;
#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
    int i, neigh, mask, type, num_z, num_m, num_o, delta = bSubtract > 0 ? -1 : 1; /* 0 or -2 => add, 1 or 2 => subtract */
    int32_t bit; /* djb-rwth: fixing coverity ID #499579 */
    int bNoAdjIon = ( bSubtract == 0 || bSubtract == 1 );
#else
    int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract ? -1 : 1;
#endif
    int bUnsatNHasTerminalO = 0;

    type = ATT_NONE;
    mask = 0;

    if (at->radical && at->radical != RADICAL_SINGLET)
    {
        goto exit_function;
    }
    
    if (is_el_a_metal( at->el_number ))
    {
        goto exit_function;
    }
    if (at->charge < -1 || at->charge > 1)
    {
        goto exit_function;
    }

    if (!at->valence && at->charge == 1 && !at->num_H && !at->radical && at->el_number == EL_NUMBER_H)
    {
        /* a proton H+ (#1) */
        type = ATT_PROTON;
        mask = ATBIT_Proton;
        goto count_mask_bits;
    }

    if (  !at->valence && at->charge == -1	&& 
          !at->num_H && !at->radical		&&
          (	at->el_number == EL_NUMBER_F || at->el_number == EL_NUMBER_CL	||
            at->el_number == EL_NUMBER_BR || at->el_number == EL_NUMBER_I	 ) 
        )
    {
        /* a halogen anion Hal- (#2) */
        type = ATT_HalAnion;
        mask = ATBIT_HalAnion;
        goto count_mask_bits;
    }
#if ( HAL_ACID_H_XCHG == 1 )
    /* halogen/chalcogen acid */
    if ((!at->valence && at->charge == 0 && 1 == at->num_H && !at->radical &&
        ( at->el_number == EL_NUMBER_F ||
          at->el_number == EL_NUMBER_CL ||
          at->el_number == EL_NUMBER_BR ||
          at->el_number == EL_NUMBER_I )) ||
         (!at->valence && at->charge == 0 && 2 == at->num_H && !at->radical &&
         ( at->el_number == EL_NUMBER_O ||
           at->el_number == EL_NUMBER_S ||
           at->el_number == EL_NUMBER_SE ||
           at->el_number == EL_NUMBER_TE )) /* djb-rwth: addressing LLVM warning */
         )
    {
        /* a halogen/chalcogen acid (#3) */
        type = ATT_HalAcid;
        mask = ATBIT_HalAcid;
        goto count_mask_bits;
    }
#endif

    if (detect_unusual_el_valence( at->el_number, at->charge, at->radical,
                                   at->chem_bonds_valence, at->num_H, at->valence ))
    {
        goto exit_function; /* unusual valence state */
    }

    /* Check neighbors */
    for (i = 0, num_z = 0, num_m = 0, num_o = 0; i < at->valence; i++)
    {
        neigh = at->neighbor[i];
#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
        if (atom[neigh].charge < -1 || atom[neigh].charge > 1)
        {
            goto exit_function; /* neighboring charge */
        }
        if (atom[neigh].charge && at->charge)
        {
            if (bNoAdjIon)
            {
#if ( FIX_RENUM_BUG_FOR_CASE_OF_ACIDIC_OH_AT_P_PLUS == 1 )
                goto count_mask_bits;
                /*update_some_attype_totals(nAtTypeTotals, mask, delta, at->charge);*/
#endif
                goto exit_function; /* neighboring charge */
            }
            type = ATT_NONE;
            mask = 0;
            goto count_mask_bits;
        }
#else
        if (atom[neigh].charge < -1 || atom[neigh].charge > 1 || atom[neigh].charge && at->charge)
        {
            goto exit_function; /* neighboring charge */
        }
#endif
        if (detect_unusual_el_valence( atom[neigh].el_number, atom[neigh].charge, atom[neigh].radical,
                                       atom[neigh].chem_bonds_valence, atom[neigh].num_H,
                                       atom[neigh].valence ))
        {
            /* neighbor in unusual valence state */
            goto exit_function;
        }
        if (is_Z_atom( atom[neigh].el_number ))
        {
            num_z++;
        }
        if (is_el_a_metal( atom[neigh].el_number ))
        {
            num_m++;
        }
        num_o += ( atom[neigh].el_number == EL_NUMBER_O );
        if (at->el_number == EL_NUMBER_N && at->valence == 2 && !at->charge &&
             /*at->valence < at->chem_bonds_valence &&*/
             atom[neigh].valence == 1 && atom[neigh].chem_bonds_valence == 2 &&
             ( atom[neigh].el_number == EL_NUMBER_O ||
               atom[neigh].el_number == EL_NUMBER_S ||
               atom[neigh].el_number == EL_NUMBER_SE ||
               atom[neigh].el_number == EL_NUMBER_TE ))
        {
            bUnsatNHasTerminalO++;
        }
    } /* eof check neighbors */

    /* O, S, Se, Te */
    if ( at->el_number == EL_NUMBER_O	||
         at->el_number == EL_NUMBER_S	||
         at->el_number == EL_NUMBER_SE	||
         at->el_number == EL_NUMBER_TE	 )
    {
        if (at->charge == 1)
        {
            if (at->num_H)
            {
                /* #4 */
                type = ATT_O_PLUS;
                mask |= ATBIT_OH_Plus;
            }
            else
            {
                /* #5 */
                type = ATT_O_PLUS;
                mask |= ATBIT_O_Plus;
            }
        }
        else
        {
            /* at->charge != 1 */
            if (at->valence > 1)
            {
                goto exit_function;  /* not a terminal atom #C1 */
            }
            else
            {
                if (at->valence && !( num_z || num_o ))
                {
                    if (num_m == at->valence)
                    {
                        goto exit_function; /* #C2 */
                    }
                    goto count_mask_bits;  /* #C3 count charges, no donor or acceptor found */
                }
                else
                {
                    /* Here at->neigh[0] is one of: O, or Z={C, N, P, As, Sb, S, Se, Te, Cl, Br, I} */
                    if (at->valence)
                    {
                        neigh = at->neighbor[0]; /* Z or O only */
                        if (!atom[neigh].charge && atom[neigh].el_number == EL_NUMBER_C &&
                             atom[neigh].chem_bonds_valence > atom[neigh].valence)
                        {
                            /* =C-OH, #C-OH, =C-O(-), #C-O(-), -C=O, =C=O; O = O, S, Se, Te */
                            type = ATT_ACIDIC_CO;
                            if (at->num_H == 1)
                            {
                                mask |= ( ATBIT_COH );            /* #6: =C-OH, #C-OH; O=O,S,Se,Te */
                                                                  /*nAtTypeTotals[ATTOT_NUM_COH] ++;*/
                            }
                            else
                            {
                                if (at->charge == -1)
                                {
                                    mask |= ( ATBIT_CO_Minus );       /* #7: =C-O(-), #C-O(-); O=O,S,Se,Te */
                                                                      /*nAtTypeTotals[ATTOT_NUM_CO_Minus] ++;*/
                                }
                                else
                                {
                                    if (!at->num_H && !at->charge)
                                    {
                                        mask |= ( ATBIT_CO );             /* #8 -C=O, =C=O; O=O,S,Se,Te */
                                                                          /*nAtTypeTotals[ATTOT_NUM_CO] ++;*/
                                    }
                                    else
                                    {
                                        mask |= ( ATBIT_Errors );
                                        /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (!atom[neigh].charge &&
                                ( atom[neigh].el_number == EL_NUMBER_O ||
                                  atom[neigh].el_number == EL_NUMBER_S ||
                                  atom[neigh].el_number == EL_NUMBER_SE ||
                                  atom[neigh].el_number == EL_NUMBER_TE ) &&
                                 atom[neigh].chem_bonds_valence == atom[neigh].valence)
                            {
                                /* -O-OH, -O-O(-); O = O, S, Se, Te */
                                type = ATT_OO;
                                if (at->num_H == 1)
                                {
                                    mask |= ( ATBIT_OOH );            /* #9 -O-OH */
                                                                      /*nAtTypeTotals[ATTOT_NUM_OOH] ++;*/
                                }
                                else
                                {
                                    if (at->charge == -1)
                                    {
                                        mask |= ( ATBIT_OO_Minus );       /* #10 -O-O(-) */
                                                                          /*nAtTypeTotals[ATTOT_NUM_OO_Minus] ++;*/
                                    }
                                    else
                                    {
                                        mask |= ( ATBIT_Errors );
                                        /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                    }
                                }
                            }
                            else
                            {
                                if (!atom[neigh].charge &&
                                     atom[neigh].chem_bonds_valence == atom[neigh].valence &&
                                     atom[neigh].el_number == EL_NUMBER_C &&
                                     at->el_number != EL_NUMBER_O)
                                {
                                    /* >C-S(-), >C-SH; S = S, Se, Te  */
                                    type = ATT_ACIDIC_S;
                                    if (at->num_H == 1)
                                    {
                                        mask |= ( ATBIT_CSH );            /* #11: >C-SH, >CH-SH, -CH2-SH; S = S, Se, Te */
                                                                          /*nAtTypeTotals[ATTOT_NUM_CSH] ++;*/
                                    }
                                    else
                                    {
                                        if (at->charge == -1)
                                        {
                                            mask |= ( ATBIT_CS_Minus );       /* #12: >C-S(-), >CH-S(-), -CH2-S(-); S = S, Se, Te  */
                                                                              /*nAtTypeTotals[ATTOT_NUM_CS_Minus] ++;*/
                                        }
                                        else
                                        {
                                            mask |= ( ATBIT_Errors );
                                            /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                        }
                                    }
                                }
                                else
                                {
                                    if (atom[neigh].el_number == EL_NUMBER_N &&
                                         atom[neigh].valence == 2 &&
                                         ( !atom[neigh].num_H || (atom[neigh].num_H == 1 && atom[neigh].charge == 1) )) /* djb-rwth: addressing LLVM warning */
                                    {
                                        /* N or N(-) or NH(+) neighbor */
                                        type = ATT_NO; /* single bond only */
                                        if (at->num_H == 1)
                                        {
                                            mask |= ( ATBIT_NOH );            /* #13: =N-OH, =NH(+)-OH, #N(+)-OH, -N(-)-OH; O = O, S, Se, Te */
                                                                              /*nAtTypeTotals[ATTOT_NUM_NOH] ++;*/
                                        }
                                        else
                                        {
                                            if (at->charge == -1)
                                            {
                                                mask |= ( ATBIT_NO_Minus );       /* #14: =N-O(-); O = O, S, Se, Te */
                                                                                  /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/
                                            }
                                            else
                                            {
                                                if (atom[neigh].charge == 1 || atom[neigh].charge == 0)
                                                {
                                                    mask |= ( ATBIT_NO );             /* #15: =N(+)=O, -NH(+)=O -N=O */
                                                                                      /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/
                                                }
                                                else
                                                {
                                                    mask |= ( ATBIT_Errors );
                                                    /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (atom[neigh].el_number == EL_NUMBER_N)
                                        {
                                            type = ATT_N_O; /* #16: single bond only */
                                            if (at->num_H == 1)
                                            {
                                                mask |= ( ATBIT_N_OH );            /* #16: -NH-OH, >N-OH or >N(+)<OH; O = O, S, Se, Te */
                                                                                   /*nAtTypeTotals[ATTOT_NUM_NOH] ++;*/
                                            }
                                            else
                                            {
                                                if (at->charge == -1)
                                                {
                                                    mask |= ( ATBIT_N_O_Minus );       /* #17: -NH-O(-), >N-O(-); O = O, S, Se, Te */
                                                                                       /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/
                                                }
                                                else
                                                {
                                                    if (atom[neigh].charge == 1)
                                                    {
                                                        mask |= ( ATBIT_N_O );             /* #18:  >N(+)=O */
                                                                                           /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/
                                                    }
                                                    else
                                                    {
                                                        mask |= ( ATBIT_Errors );
                                                        /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if ( atom[neigh].el_number != EL_NUMBER_C &&
                                                 atom[neigh].el_number != EL_NUMBER_O &&
                                                 !is_el_a_metal( atom[neigh].el_number ) &&
                                                 atom[neigh].chem_bonds_valence > atom[neigh].valence)
                                            {
                                                /* =Z-OH, #Z-OH, =Z-O(-), #Z-O(-), -Z=O, =Z=O;
                                                =Z(+)-OH, #Z(+)-OH, =Z-O(-), #Z-O(-), -Z(+)=O, =Z(+)=O; O = O, S, Se, Te */
                                                /* neigh = Z\{N,C} = P, As, Sb, S, Se, Te, Cl, Br, I */
                                                if (at->chem_bonds_valence == 1 && IsZOX( atom, at_no, 0 ))
                                                {
                                                    type = ATT_ZOO;
                                                    if (at->num_H == 1)
                                                    {
                                                        mask |= ( ATBIT_ZOOH );            /* 18: O=Z-OH; O=O,S,Se,Te; Z may have charge */
                                                                                           /*nAtTypeTotals[ATTOT_NUM_ZOOH] ++;*/
                                                    }
                                                    else
                                                    {
                                                        if (at->charge == -1)
                                                        {
                                                            mask |= ( ATBIT_ZOO_Minus );       /* 19: O=Z-O(-); O = O, S, Se, Te */
                                                                                               /*nAtTypeTotals[ATTOT_NUM_ZOO_Minus] ++;*/
                                                        }
                                                        else
                                                        {
                                                            mask |= ( ATBIT_Errors );
                                                            /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    type = ATT_OTHER_ZO;
                                                    if (at->num_H == 1)
                                                    {
                                                        mask |= ( ATBIT_ZOH );            /* 20: =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge */
                                                                                          /*nAtTypeTotals[ATTOT_NUM_ZOH] ++;*/
                                                    }
                                                    else
                                                    {
                                                        if (at->charge == -1)
                                                        {
                                                            mask |= ( ATBIT_ZO_Minus );       /* 21: =Z-O(-), #Z-O(-); O = O, S, Se, Te */
                                                                                              /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/
                                                        }
                                                        else
                                                        {
                                                            if (at->num_H == 0)
                                                            {
                                                                mask |= ( ATBIT_ZO );             /* 22: -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */
                                                                                                  /*nAtTypeTotals[ATTOT_NUM_ZO] ++;*/
                                                            }
                                                            else
                                                            {
                                                                mask |= ( ATBIT_Errors );
                                                                /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                if (at->charge == -1 && !is_el_a_metal( atom[neigh].el_number ))
                                                {
                                                    /* >Z-O(-); O=O,S,Se,Te */
                                                    type = ATT_OTHER_NEG_O;
                                                    mask |= ( ATBIT_O_Minus );            /* 23: -Z-O(-); O=O,S,Se,Te */
                                                                                          /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    else
                    {
                        if (at->charge == -1 && at->num_H == 1)
                        {
                            type = ATT_OH_MINUS;
                            mask |= ( ATBIT_O_Minus );            /* 25: HO(-); O=O,S,Se,Te */
                        }
                    }
                }
            }
        }
    }

    else
    {
        /* P, N, neutral valence = 3 (not 5) */
        if (( at->el_number == EL_NUMBER_N ||
              at->el_number == EL_NUMBER_P ) &&
             0 <= at->valence && at->valence <= 3 &&
             at->chem_bonds_valence + at->num_H == 3 + at->charge)
        {
            if (at->valence && !( num_z /*|| num_o == at->valence*/ ))
            {
                if (num_m == at->valence)
                {
                    goto exit_function;
                }
                goto count_mask_bits; /* N(III), N(-)(II), N(+)(IV) and same P that have only oxygen neighbors are ignored here */
            }
            type = ( at->el_number == EL_NUMBER_N ) ? ATT_ATOM_N : ATT_ATOM_P;
            switch (at->charge)
            {
                case -1:
                    if (at->el_number == EL_NUMBER_N)
                    {
                        mask |= ( ATBIT_N_Minus ); /* 26: -NH(-), =N(-), >N(-) */
                        if (at->num_H)
                        {
                            mask |= ( ATBIT_NP_H ); /* 27: -NH(-) */
                        }
#if ( FIX_NP_MINUS_BUG == 1 )
                        else
                        {
                            if (at->valence == 1 && at->chem_bonds_valence >= 2 && ( at->bond_type[0] & BOND_MARK_ALL ))
                            {
                                type |= ATT_NP_MINUS_V23; /* =N(-) created by normalization 2010-03-11 DT */
                            }
                        }
#endif
                    }
                    /*nAtTypeTotals[ATTOT_NUM_N_Minus] += (at->el_number == EL_NUMBER_N);*/
                    break;
                case 0:
                    if (at->num_H)
                    {
                        mask |= ( ATBIT_NP_H );          /* 28: -NH2, =NH, >NH */
                                                         /*nAtTypeTotals[ATTOT_NUM_NP_H] ++;*/
                    }
                    else
                    {
                        if (bUnsatNHasTerminalO == 1)
                        {
                            mask |= ( ATBIT_ON );            /* 29: -N=O,-N=OH(+) only, not =N-OH */
                        }
                        else
                        {
                            mask |= ( ATBIT_NP );            /* 30: -P=O,-P=OH(+), >N- =N- (incl. =N-OH) , #N */
                                                             /*nAtTypeTotals[ATTOT_NUM_NP] ++;*/
                        }
                    }
                    break; /* ignore neutral N or P */
                case 1:
                    if (at->num_H)
                    {
                        mask |= ( ATBIT_NP_Proton );       /* 31: NH4(+), -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+) */
                                                           /*nAtTypeTotals[ATTOT_NUM_NP_Proton] ++;*/
                    }
                    else
                    {
                        if (at->chem_bonds_valence > at->valence)
                        {
                            mask |= ( ATBIT_NP_Plus );         /* =N(+)=, #N(+)-, =N(+)< */
                                                               /*nAtTypeTotals[ATTOT_NUM_NP_Plus] ++;*/
                        }
                        else
                        {
                            type = 0; /* 32: ignore onium cations >N(+)< */
                        }
                    }
                    break;
                default:
                    mask |= ( 1 << ATTOT_NUM_Errors );
                    /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/
                    break;
            }
        }
    }

count_mask_bits:
    if (nAtTypeTotals)
    {
        if (mask && !( mask & ( ATBIT_Errors ) ))
        {
            for (i = 0, bit = 1; i < ATTOT_ARRAY_LEN; i++, bit <<= 1)
            {
                if (bit & mask)
                {
                    nAtTypeTotals[i] += delta;
                }
            }
        }

        /* Count charges */
        if (at->charge)
        {
            nAtTypeTotals[ATTOT_TOT_CHARGE] += delta * at->charge;
            nAtTypeTotals[ATTOT_NUM_CHARGES] += delta;
        }
    }

    if (pMask)
    {
        *pMask = mask;
    }

exit_function:
    if (mask & ( ATBIT_Errors ))
    {
        type = 0;
        if (nAtTypeTotals)
        {
            nAtTypeTotals[ATTOT_NUM_Errors] += 1;
        }
    }


    return type;
}


/****************************************************************************/
int SimpleRemoveHplusNPO( inp_ATOM *at,
                          int num_atoms,
                          int nAtTypeTotals[],
                          T_GROUP_INFO *t_group_info )
{
    int i, mask, type, num_removed;
    for (i = 0, num_removed = 0; i < num_atoms; i++)
    {
        /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
        if (( PR_SIMPLE_TYP & ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ) ) &&
            ( PR_SIMPLE_MSK & mask ))
        {
#if ( bRELEASE_VERSION == 0 )
            if (at[i].charge != 1 || at[i].num_H == 0)
            {
                return -1;  /* program error */
            }
#endif
            type = GetAtomChargeType(at, i, nAtTypeTotals, &mask, 1); /* subtract at[i] */ /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
            at[i].charge = 0;
            AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB) i, t_group_info );
            /*at[i].num_H --;*/
            num_removed++;
#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
            type = GetAtomChargeType(at, i, nAtTypeTotals, &mask, 0); /* add changed at[i] */ /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
#else
            type = GetAtomChargeType(at, i, nAtTypeTotals, &mask, 1); /* bug: subtract instead of add */ /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
#endif
                                                                        /*
                                                                        if ( nAtTypeTotals ) {
                                                                        nAtTypeTotals[ATTOT_NUM_NP_Proton] --;
                                                                        if ( at[i].num_H ) {
                                                                        nAtTypeTotals[ATTOT_NUM_NP_H] ++;
                                                                        } else {
                                                                        nAtTypeTotals[ATTOT_NUM_NP] ++;
                                                                        }
                                                                        nAtTypeTotals[ATTOT_TOT_CHARGE] --;
                                                                        nAtTypeTotals[ATTOT_NUM_CHARGES] --;
                                                                        }
                                                                        */
        }
    }

    return num_removed;
}


/****************************************************************************/
int bIsAtomTypeHard( inp_ATOM *at,
                     int endpoint,
                     int nType,
                     int nMask,
                     int nCharge )
{
    int        mask;
    if (( nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 ) ) && ( mask & nMask )
#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 )
         && ( at[endpoint].charge == nCharge || !at[endpoint].charge )
#endif
         )
    {
        return 1;
    }

    return 0;
}


/****************************************************************************/
int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType )
{
    if (bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_H, PR_HARD_MSK_H, 0 ))
    {
        /* Obtain donor/acceptor info */
        int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge;
        if (neutral_valence != 2 /* O, S, Se, Te */ &&
             neutral_valence != 3 /* N, P */)
        {
            return -1; /* wrong endpoint neutral valence */
        }
        else
        {
            int edge_flow = at[endpoint].num_H;
            int num_bonds = at[endpoint].valence;
            int edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */
            edge_flow = inchi_min( edge_flow, edge_cap );
            /* what this means: */
            if (edge_cap)
            {
                if (edge_cap > edge_flow)
                {
                    *cSubType |= SALT_ACCEPTOR;
                }
                if (edge_flow)
                {
                    *cSubType |= SALT_DONOR_H;
                }
                return 4;
            }
        }
    }

    return -1;
}


/****************************************************************************/
int bIsNegAtomType( inp_ATOM *at, int endpoint, int *cSubType )
{
    int sub_type = 0;
    if (bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ))
    {
        /* Obtain donor/acceptor info */
        int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge;
        if (neutral_valence != 2 /* O, S, Se, Te */ &&
             neutral_valence != 3 /* N, P */)
        {
            return -1; /* wrong endpoint neutral valence */
        }
        else
        {
            int edge_flow = ( at[endpoint].charge == -1 );
            int num_bonds = at[endpoint].valence;
            int edge_cap = neutral_valence - num_bonds - at[endpoint].num_H; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */
            edge_flow = inchi_min( edge_flow, edge_cap );
            /* what this means: */
            if (edge_cap)
            {
                if (edge_cap > edge_flow)
                {
                    sub_type |= SALT_ACCEPTOR;
                }
                if (edge_flow)
                {
                    sub_type |= SALT_DONOR_Neg;
                }
                if (sub_type)
                {
                    *cSubType |= sub_type;
                    return 4;
                }
            }
        }
    }

    return -1;
}


/****************************************************************************/
int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType )
{
    int ret1, ret2, ret;
    int sub_type = 0;
    ret1 = bIsHDonorAccAtomType( at, i, &sub_type );
    ret2 = bIsNegAtomType( at, i, &sub_type );
    ret = inchi_max( ret1, ret2 );
    if (ret > 0 && sub_type)
    {
        *cSubType |= sub_type;
        return ret;
    }
    return -1;
}


/****************************************************************************/
int CreateCGroupInBnStruct( inp_ATOM *at,
                            int num_atoms,
                            BN_STRUCT *pBNS,
                            int nType,
                            int nMask,
                            int nCharge )
{
    int         k, c_point, cg, centerpoint, fictpoint, type, ret = 0;
    int         num_cg = 1;
    int         num_edges = pBNS->num_edges;
    int         num_vertices = pBNS->num_vertices; /* new c-group bns-ID */
    BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev;  /* fictitious vertex describing charge c-group */
    BNS_VERTEX *vertex_cpoint;
    BNS_EDGE   *edge;      /* edge between that vertex and the tautomeric c_point */
    int        mask, num_CPoints;

    /* Debug: check overflow */
    if (num_vertices + num_cg >= pBNS->max_vertices)
    {
        return BNS_VERT_EDGE_OVFL;
    }

    /* Count new c-group edges */
    for (c_point = 0, num_CPoints = 0; c_point < num_atoms; c_point++)
    {
        if (( nType & GetAtomChargeType( at, c_point, NULL, &mask, 0 ) ) && ( mask & nMask )
#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 )
             && ( at[c_point].charge == nCharge || !at[c_point].charge )
#endif
             )
        {
            num_CPoints++;
        }
    }

    if (!num_CPoints)
    {
        return 0;
    }

    /* Clear the new vertex */
    memset( pBNS->vert + num_vertices, 0, 1 * sizeof( pBNS->vert[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */

    /* *old* Make sure the last t-group has the largest t-group ID:
    this is necessary to correctly add new edges and vertices for testing augmenting paths
    */

    /**************************************/
    /* initialize new fictitious vertex   */
    /* representing c-point group         */
    /**************************************/

    ver_ficpont_prev = pBNS->vert + num_vertices - 1;

    for (cg = 0; cg < num_cg; cg++, ver_ficpont_prev = vert_ficpoint)
    {
        /*
        vert_ficpoint-1 is the last vertex;
        vert_ficpoint   is the being added vertex
        Note: nGroupNumber are not contiguous
        */
        vert_ficpoint = pBNS->vert + num_vertices + cg;
        vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges;
        vert_ficpoint->max_adj_edges = num_CPoints + BNS_ADD_EDGES;
        vert_ficpoint->num_adj_edges = 0;
        vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0;
        vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0;
        vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP | ( ( nCharge < 0 ) ? BNS_VERT_TYPE_C_NEGATIVE : 0 );
    }

    /************************************************/
    /* Connect c-points to the fictitious vertices  */
    /* representing c-point groups; set caps, flows */
    /************************************************/
    cg = 1;
    for (c_point = 0; c_point < num_atoms; c_point++)
    {
        if (( nType & ( type = GetAtomChargeType( at, c_point, NULL, &mask, 0 ) ) ) && ( mask & nMask )
#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 )
             && ( at[c_point].charge == nCharge || !at[c_point].charge )
#endif
             )
        {
            ;
        }
        else
        {
            continue;
        }

        fictpoint = cg + num_vertices - 1; /* c-group vertex index */
        vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */
        vertex_cpoint = pBNS->vert + c_point;   /* c_point vertex */

                                                /* Debug: check overflow */
        if (fictpoint >= pBNS->max_vertices ||
             num_edges >= pBNS->max_edges ||
             vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges ||
             vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges)
        {
            /* djb-rwth: removing redundant code */
            break;
        }
        vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT;
        if (( KNOWN_ACIDIC_TYPE & type ) && nCharge < 0)
        {
            vertex_cpoint->type |= pBNS->type_TACN;
        }

#if ( FIX_CPOINT_BOND_CAP != 1 )  /* { */
        /* Set capacity = 1 to the edges from the c_point to the centerpoint(s)     */
        /* if their current capacity is zero                                        */
        /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */
        for (k = 0; k < vertex_cpoint->num_adj_edges; k++)
        {
            int iedge = vertex_cpoint->iedge[k];
            if (!pBNS->edge[iedge].cap)
            {
                /* single bond, possibly between c_point and centerpoint */
                centerpoint = ( pBNS->edge[iedge].neighbor12 ^ c_point );
                if (centerpoint < pBNS->num_atoms &&
                     pBNS->vert[centerpoint].st_edge.cap >= 1)
                {
                    int bond_type = ( at[c_point].bond_type[k] & BOND_TYPE_MASK );
                    if (bond_type == BOND_TAUTOM ||
                         bond_type == BOND_ALTERN ||
                         bond_type == BOND_SINGLE)
                    {
                        pBNS->edge[iedge].cap = 1;
                    }
                }
            }
        }
#endif /* } FIX_CPOINT_BOND_CAP */

        /* Create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */
        edge = pBNS->edge + num_edges;
        edge->cap = 1;
        edge->flow = 0;
        edge->pass = 0;
#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
        edge->forbidden &= pBNS->edge_forbidden_mask; /* remove previous temporary ban */
#endif

                                                      /* nCharge = +1: mark edge to c-point having no (+)-moveable charge with flow=1 */
                                                      /* nCharge = -1: mark edge to c-point having -1 moveable charge with flow=1 */

        if ((nCharge == 1 && at[c_point].charge != 1) || (nCharge == -1 && at[c_point].charge == -1)) /* djb-rwth: addressing LLVM warning */
            /*if ( !CHARGED_CPOINT(at,c_point) )*/
        {
            /* Increment new edge flow, update st_edges of the adjacent vertices */
            edge->flow++;
            /* Increment c-group vertex st-flow & cap */
            vert_ficpoint->st_edge.flow++;
            vert_ficpoint->st_edge.cap++;
            /* Increment c-point vertex st-flow & cap */
            vertex_cpoint->st_edge.flow++;
            vertex_cpoint->st_edge.cap++;
        }

#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */
        /* Set capacity = 1 to the edges from the c_point to the centerpoint(s)     */
        /* if their current capacity is zero                                        */
        /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */
        for (k = 0; k < vertex_cpoint->num_adj_edges; k++)
        {
            int iedge = vertex_cpoint->iedge[k];
            VertexFlow  nNewCap = vertex_cpoint->st_edge.cap;
            centerpoint = ( pBNS->edge[iedge].neighbor12 ^ c_point );
            if (!pBNS->edge[iedge].cap)
            {
                /* Single bond, possibly between c_point and centerpoint */
                if (centerpoint < pBNS->num_atoms &&
                     pBNS->vert[centerpoint].st_edge.cap >= 1)
                {
                    nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap );
                    nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP );
                    pBNS->edge[iedge].cap = nNewCap;
                }
            }
#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */
            else
            {
                if (centerpoint < pBNS->num_atoms &&
                     edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP)
                {
                    pBNS->edge[iedge].cap++;
                }
            }
#endif
        }
#endif  /* } FIX_CPOINT_BOND_CAP */

        /* Connect edge to c_point and fictpoint and increment the counters of neighbors and edges */
        edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */
        edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */
        vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges;
        vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges++;
        edge->neigh_ord[0] = vertex_cpoint->num_adj_edges++;
        edge->neigh_ord[1] = vert_ficpoint->num_adj_edges++;
        edge->cap0 = edge->cap;
        edge->flow0 = edge->flow;
    }

    ret = pBNS->num_vertices; /* new c-group atom number */
    pBNS->num_edges = num_edges;
    pBNS->num_vertices += num_cg;
    pBNS->num_c_groups += num_cg;

    return ret;
}


/****************************************************************************/
int CreateTGroupInBnStruct( inp_ATOM *at,
                            int num_atoms,
                            BN_STRUCT *pBNS,
                            int nType,
                            int nMask )
{
    int ret = 0;
    /* ret = ReInitBnStruct( pBNS ); */
    int         k, endpoint, tg, centerpoint, fictpoint;
    int         num_tg = 1;
    int         num_edges = pBNS->num_edges;
    int         num_vertices = pBNS->num_vertices;
    BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev;  /* fictitious vertex describing t-group */
    BNS_VERTEX *vert_endpoint;
    BNS_EDGE   *edge;      /* edge between that vertex and the tautomeric endpoint */
    int        mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds;

    /* Debug: check overflow */
    if (num_vertices + num_tg >= pBNS->max_vertices)
    {
        return BNS_VERT_EDGE_OVFL;
    }

    /* Count new t-group edges */
    for (endpoint = 0, num_endpoints = 0; endpoint < num_atoms; endpoint++)
    {
        if (( nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 ) ) && ( mask & nMask )
             )
        {
            num_endpoints++;
        }
    }

    if (!num_endpoints)
    {
        return 0;
    }

    /* Since t-group IDs may be not contiguous, clear all vertices that will be added.
    all-zeroes-vertex will be ignored by the BNS
    */
    memset( pBNS->vert + num_vertices, 0, num_tg * sizeof( pBNS->vert[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */

    /* *old* Make sure the last t-group has the largest t-group ID:
    this is necessary to correctly add new edges and vertices for testing augmenting paths
    */

    /**************************************/
    /* Initialize new fictitious vertex   */
    /* representing t-point group         */
    /**************************************/
    ver_ficpont_prev = pBNS->vert + num_vertices - 1;

    for (tg = 0; tg < num_tg; tg++, ver_ficpont_prev = vert_ficpoint)
    {
        /*
        vert_ficpoint-1 is the last vertex;
        vert_ficpoint   is the vertex that is being added
        Note: nGroupNumber are not contiguous
        */
        vert_ficpoint = pBNS->vert + num_vertices + tg;
        vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges;
        vert_ficpoint->max_adj_edges = num_endpoints + BNS_ADD_EDGES + BNS_ADD_SUPER_TGROUP;
        vert_ficpoint->num_adj_edges = 0;
        vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0;
        vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0;
        vert_ficpoint->type |= BNS_VERT_TYPE_TGROUP;
    }

    tg = 1;
    for (endpoint = 0; endpoint < num_atoms; endpoint++)
    {
        if (( nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 ) ) && ( mask & nMask ))
        {
            ;
        }
        else
        {
            continue;
        }
        fictpoint = tg + num_vertices - 1;
        vert_ficpoint = pBNS->vert + fictpoint;
        vert_endpoint = pBNS->vert + endpoint;
        /* Debug: check overflow */
        if (fictpoint >= pBNS->max_vertices ||
             num_edges >= pBNS->max_edges ||
             vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges ||
             vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges)
        {
            /* djb-rwth: removing redundant code */
            break;
        }

        /* Obtain donor/acceptor info */
        neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge;
        if (neutral_valence != 2 /* O, S, Se, Te */ &&
             neutral_valence != 3 /* N, P */)
        {
            /* djb-rwth: removing redundant code */
            break;
        }
        edge_flow = at[endpoint].num_H;
        num_bonds = at[endpoint].valence;
        edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */

        if (3 == neutral_valence /* N or P */ && 1 < num_bonds)
        {
            edge_cap++; /* allow -NH2(+)- => -N=, >NH(+)- => >N- */
        }
        edge_flow = inchi_min( edge_flow, edge_cap );
        /*
        if ( !nGetEndpointInfo( at, endpoint, &eif ) ) {
        ret = BNS_BOND_ERR;
        break;
        }
        */
        vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT;

        /* Create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */
        edge = pBNS->edge + num_edges;
        edge->cap = edge_cap;
        edge->flow = edge_flow;
        edge->pass = 0;
#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
        edge->forbidden &= pBNS->edge_forbidden_mask;
#endif

        /* Adjust st_flow and st_cap of the adjacent vertices */
        /* Adjust t-group vertex st-flow & cap */
        vert_ficpoint->st_edge.flow += edge->flow;
        vert_ficpoint->st_edge.cap += edge->flow;
        /* adjust endpoint vertex st-flow & cap */
        vert_endpoint->st_edge.flow += edge->flow;
        vert_endpoint->st_edge.cap += edge->flow;

        /* Adjust edge cap & flow according to the number of H and number of bonds */
        for (k = 0; k < vert_endpoint->num_adj_edges; k++)
        {
            int iedge = vert_endpoint->iedge[k];
            VertexFlow  nNewCap = vert_endpoint->st_edge.cap;
            if (!pBNS->edge[iedge].cap)
            {
                /* single bond, possibly between endpoint and centerpoint */
                centerpoint = ( pBNS->edge[iedge].neighbor12 ^ endpoint );
                if (centerpoint < pBNS->num_atoms &&
                     pBNS->vert[centerpoint].st_edge.cap >= 1)
                {
                    nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap );
                    nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP );
                    pBNS->edge[iedge].cap = nNewCap;
                }
            }
        }

        /* Connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */
        edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */
        edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */
        vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges;
        vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges++;
        edge->neigh_ord[0] = vert_endpoint->num_adj_edges++;
        edge->neigh_ord[1] = vert_ficpoint->num_adj_edges++;
        edge->cap0 = edge->cap;
        edge->flow0 = edge->flow;
    }

    ret = pBNS->num_vertices; /* new t-group atom number */
    pBNS->num_edges = num_edges;
    pBNS->num_vertices += num_tg;
    pBNS->num_t_groups += num_tg;

    return ret;
}


/****************************************************************************/
int RemoveLastGroupFromBnStruct( inp_ATOM *at,
                                 int num_atoms,
                                 int tg,
                                 BN_STRUCT *pBNS )
{
    int ret = 0;
    /* ret = ReInitBnStruct( pBNS ); */
    int         k, endpoint, /*centerpoint, fictpoint,*/ iedge;
    int         num_edges = pBNS->num_edges;
    int         num_vertices = pBNS->num_vertices;
    BNS_VERTEX *vert_ficpoint /*, *ver_ficpont_prev*/;  /* fictitious vertex describing t-group */
    BNS_VERTEX *vert_endpoint;
    BNS_EDGE   *edge;      /* edge between that vertex and the tautomeric endpoint */
                           /*int        mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds;*/
    int        is_t_group = 0, is_c_group = 0;

    /* Debug: check overflow */
    if (pBNS->num_added_atoms + pBNS->num_c_groups + pBNS->num_t_groups + num_atoms >= pBNS->max_vertices)
    {
        return BNS_VERT_EDGE_OVFL;
    }

    if (tg + 1 != num_vertices)
    {
        return BNS_VERT_EDGE_OVFL;
    }

    vert_ficpoint = pBNS->vert + tg;

    if (vert_ficpoint->type & BNS_VERT_TYPE_TGROUP)
    {
        is_t_group = 1;
    }
    if (vert_ficpoint->type & BNS_VERT_TYPE_C_GROUP)
    {
        is_c_group = 1;
        if (vert_ficpoint->type & BNS_VERT_TYPE_C_NEGATIVE)
        {
            is_c_group = 2;
        }
    }

    for (k = vert_ficpoint->num_adj_edges - 1; 0 <= k; k--)
    {
        iedge = vert_ficpoint->iedge[k];
        if (iedge + 1 != num_edges)
        {
            return BNS_VERT_EDGE_OVFL;
        }
        edge = pBNS->edge + iedge;
        endpoint = edge->neighbor12 ^ tg;
        vert_endpoint = pBNS->vert + endpoint;
        /* adjust st_flow, st_cap */
        vert_endpoint->st_edge.cap0 =
            vert_endpoint->st_edge.cap -= edge->flow;
        vert_endpoint->st_edge.flow0 =
            vert_endpoint->st_edge.flow -= edge->flow;
        if (pBNS->type_TACN && ( vert_endpoint->type & pBNS->type_TACN ) == pBNS->type_TACN)
        {
            vert_endpoint->type ^= pBNS->type_TACN;
        }
        if (is_t_group)
        {
            vert_endpoint->type ^= ( vert_ficpoint->type & BNS_VERT_TYPE_ENDPOINT );
        }
        if (is_c_group)
        {
            vert_endpoint->type ^= ( vert_ficpoint->type & BNS_VERT_TYPE_C_POINT );
        }
        /* Remove edge */
        if (edge->neigh_ord[0] + 1 != vert_endpoint->num_adj_edges)
        {
            return BNS_VERT_EDGE_OVFL;
        }
        vert_endpoint->num_adj_edges--;
        memset( edge, 0, sizeof( *edge ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        num_edges--;
        if (1 == is_t_group && endpoint < num_atoms)
        {
            at->endpoint = 0;
        }
        if (1 == is_c_group && endpoint < num_atoms)
        {
            at->c_point = 0;
        }
    }
    memset( vert_ficpoint, 0, sizeof( *vert_ficpoint ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    num_vertices--;

    pBNS->num_edges = num_edges;
    pBNS->num_vertices = num_vertices;
    if (is_t_group)
    {
        pBNS->num_t_groups--;
    }
    if (is_c_group)
    {
        pBNS->num_c_groups--;
    }

    return ret;
}



/****************************************************************************/
int SetInitCapFlowToCurrent( BN_STRUCT *pBNS )
{
    int       i, j;
    BNS_EDGE *pEdge = NULL;
    for (i = 0; i < pBNS->num_vertices; i++)
    {
        pBNS->vert[i].st_edge.flow0 = pBNS->vert[i].st_edge.flow;
        pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap;
        for (j = 0; j < pBNS->vert[i].num_adj_edges; j++)
        {
            pEdge = pBNS->edge + pBNS->vert[i].iedge[j];
            pEdge->cap0 = pEdge->cap;
            pEdge->flow0 = pEdge->flow;
        }
    }

    return 0;
}


int ArTypMask[] =
{
    AR_SIMPLE_TYP1,
    AR_SIMPLE_MSK1,
    AR_SIMPLE_TYP2,
    AR_SIMPLE_MSK2,
    AR_SIMPLE_TYP3,
    AR_SIMPLE_MSK3,
    0,
    0
};



/****************************************************************************/
int SimpleRemoveAcidicProtons( inp_ATOM *at,
                               int num_atoms,
                               BN_AATG *pAATG,
                               int num2remove )
{
    int i, j, max_j = -1, mask, type, num_removed;
    int num[AR_SIMPLE_STEPS + 1], num_tot;

    for (j = 0; ArTypMask[2 * j]; j++)
    {
        num[max_j = j] = 0;
    }

    for (i = 0; i < num_atoms; i++)
    {
        if (!at[i].charge && at[i].num_H && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
        {
            for (j = 0; j <= max_j; j++)
            {
                if (( type & ArTypMask[2 * j] ) && ( mask && ArTypMask[2 * j + 1] ))
                {
                    num[j]  ++;
                    break;
                }
            }
        }
    }
    for (j = 0, num_tot = 0; j <= max_j; j++)
    {
        if (( num_tot += num[j] ) >= num2remove)
        {
            max_j = j;
            break;
        }
    }
    if (!num_tot)
    {
        return 0;
    }
    for (i = 0, num_removed = 0; i < num_atoms && num_removed < num2remove; i++)
    {
        if (!at[i].charge && at[i].num_H && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
        {
            for (j = 0; j <= max_j; j++)
            {
                if (num[j] && ( type & ArTypMask[2 * j] ) && ( mask && ArTypMask[2 * j + 1] ))
                {
                    /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                    type = GetAtomChargeType(at, i, pAATG->nAtTypeTotals, &mask, 1); /* subtract at[i] */ 
                    num[j]  --;
                    at[i].charge--;
                    AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB) i, pAATG->t_group_info );
                    /*at[i].num_H  --;*/
                    num_removed++;
                    type = GetAtomChargeType(at, i, pAATG->nAtTypeTotals, &mask, 0); /* add changed at[i] */ /* ! THIS CHANGES pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] */ 
                    break;
                }
            }
        }
    }

    /*
    pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]  -= num_removed;
    pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] += num_removed;
    */

    return num_removed;
}


/****************************************************************************/
int bHasAcidicHydrogen( inp_ATOM *at, int i )
{
    int bFound = 0, j, type, mask;
    if (!at[i].charge && at[i].num_H && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
    {
        for (j = 0; ArTypMask[2 * j]; j++)
        {
            if (( type & ArTypMask[2 * j] ) && ( mask & ArTypMask[2 * j + 1] ))
            {
                bFound++;
                break;
            }
        }
    }

    return bFound;
}


/****************************************************************************/
int bHasOtherExchangableH( inp_ATOM *at, int i )
{
    int bFound = 0, type, mask;
    if (at[i].num_H && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
    {
        if (( type & ATT_ATOM_N ) && ( mask & ATBIT_NP_H ))
        {
            bFound++;
        }
    }

    return bFound;
}


int AaTypMask[] =
{
    AA_SIMPLE_TYP1,
    AA_SIMPLE_MSK1,
#if ( FIX_NP_MINUS_BUG == 1 )
    AA_SIMPLE_TYP4,
    AA_SIMPLE_MSK4,   /* should not follow 0,0 pair */
#endif
    AA_SIMPLE_TYP2,
    AA_SIMPLE_MSK2,
    AA_SIMPLE_TYP3,
    AA_SIMPLE_MSK3,
    0,
    0
};


/****************************************************************************/
int SimpleAddAcidicProtons( inp_ATOM *at,
                            int num_atoms,
                            BN_AATG *pAATG, 
                            int num2add )
{
    int i, j, max_j = -1, mask, type, num_added;
    int num[AR_SIMPLE_STEPS + 1], num_tot;

    for (j = 0; AaTypMask[2 * j]; j++)
    {
        num[max_j = j] = 0;
    }

    for (i = 0; i < num_atoms; i++)
    {
        if (at[i].charge == -1 && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
        {
            for (j = 0; j <= max_j; j++)
            {
                if (( type & AaTypMask[2 * j] ) && ( mask && AaTypMask[2 * j + 1] ))
                {
                    num[j]  ++;
                    break;
                }
            }
        }
    }
    for (j = 0, num_tot = 0; j <= max_j; j++)
    {
        if (( num_tot += num[j] ) >= num2add)
        {
            max_j = j;
            break;
        }
    }
    if (!num_tot)
    {
        return 0;
    }
    for (i = 0, num_added = 0; i < num_atoms && num_added < num2add; i++)
    {
        if (at[i].charge == -1 && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
        {
            for (j = 0; j <= max_j; j++)
            {
                if (num[j] && ( type & AaTypMask[2 * j] ) && ( mask && AaTypMask[2 * j + 1] ))
                {
                    /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                    type = GetAtomChargeType(at, i, pAATG->nAtTypeTotals, &mask, 1); /* subtract at[i] */
                    num[j]  --;
                    at[i].charge++;
                    AddOrRemoveExplOrImplH( 1, at, num_atoms, (AT_NUMB) i, pAATG->t_group_info );
                    /*at[i].num_H  ++;*/
                    num_added++;
                    type = GetAtomChargeType(at, i, pAATG->nAtTypeTotals, &mask, 0); /* add changed at[i] */
                    break;
                }
            }
        }
    }

    /*
    pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]  += num_added;
    pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] -= num_added;
    */

    return num_added;
}


/****************************************************************************/
int bHasAcidicMinus( inp_ATOM *at, int i )
{
    int bFound = 0, j, type, mask;
    if (at[i].charge == -1 && ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ))
    {
        for (j = 0; AaTypMask[2 * j]; j++)
        {
            if (( type & AaTypMask[2 * j] ) && ( mask & AaTypMask[2 * j + 1] ))
            {
                bFound++;
                break;
            }
        }
    }

    return bFound;
}


/****************************************************************************
HardRemoveAcidicProtons( ... )

Create 2 tautomeric groups:
(1) for O on -C=O,
(2) for the rest of the atoms.
Pull H from (2) to (1); remove later
****************************************************************************/
int HardRemoveAcidicProtons( CANON_GLOBALS *pCG,
                             inp_ATOM *at,
                             int num_atoms,
                             BN_AATG *pAATG,
                             int num2remove,
                             int *nNumCanceledCharges,
                             BN_STRUCT *pBNS,
                             BN_DATA *pBD )
{
    int cg_Plus = 0;
    int cg_Minus = 0;
    int tg_H_Other = 0;
    int tg_H_Acid = 0;

    int ret = 0, ret2;
    int nDelta, nNumMoved2AcidH = 0, nNumNeutralized = 0, nPrevNumCharges; /* djb-rwth: removing redundant variables */

    int nPosCharges, nPosCharges2;
    int nNegCharges, nNegCharges2;
    /*
    int nNumNP_H, nNumNP_H2;
    int nNumOS_H, nNumOS_H2;
    */

    nPosCharges = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    nNegCharges = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    /*
    nNumNP_H    =  pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] +
    pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton];
    nNumOS_H    =  pAATG->nAtTypeTotals[ATTOT_NUM_COH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_CSH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_ZOH];
    */

    /* Prevent free exchange H <-> (-) */
    pBNS->type_CN = ( BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE );
    pBNS->type_T = BNS_VERT_TYPE_TGROUP;
    pBNS->type_TACN = BNS_VERT_TYPE_ACID;

    /* Create (+) charge group */
    cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_POS, AR_HARD_MSK_POS, 1 );

    /* create (-) charge group */
    /*
    if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] +
    nAtTypeTotals[ATTOT_NUM_CS_Minus] +
    nAtTypeTotals[ATTOT_NUM_ZO_Minus] +
    nAtTypeTotals[ATTOT_NUM_N_Minus] )
    */

    cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_NEG, AR_HARD_MSK_NEG, -1 );

    pBNS->type_CN = ( BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE );
    pBNS->type_T = BNS_VERT_TYPE_TGROUP;
    pBNS->type_TACN = BNS_VERT_TYPE_ACID;

    /* Create tautomeric group for non-acidic or negatively charged acidic O  */
    tg_H_Other = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HN, AR_HARD_MSK_HN );

    /* Create tautomeric group for possibly acidic O */
    tg_H_Acid = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HA, AR_HARD_MSK_HA );
    if (tg_H_Other >= num_atoms && tg_H_Acid >= num_atoms)
    {
        /* Find alt path to remove one proton */
        do
        {
            /* Remove a proton */
            nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
            /* djb-rwth: addressing coverity ID #499503 -- tg_H_Other negative values handled properly */
            ret = bExistsAltPath( pCG, pBNS, pBD, pAATG,
                                  at, num_atoms,
                                  tg_H_Other /*nVertDoubleBond*/,
                                  tg_H_Acid /*nVertSingleBond*/,
                                  ALT_PATH_MODE_REM_PROTON );
            if (IS_BNS_ERROR( ret ))
            {
                return ret;
            }
            if (ret & 1)
            {
                nDelta = ( ret & ~3 ) >> 2;
                /* djb-rwth: removing redundant code */
                if (nDelta)
                {
                    /* Radical pair has disappeared */
                    ; /* goto quick_exit;*/
                }
                nNumMoved2AcidH++;
                if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1)
                {
                    nNumNeutralized += ( nPrevNumCharges - ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1 ) ) / 2;
                }
            }
        } while (( ret & 1 ) && nNumMoved2AcidH < num2remove);

        /* Neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */
        if (( nNumMoved2AcidH /*|| bCancelChargesAlways*/ ) && cg_Minus >= num_atoms && cg_Plus >= num_atoms &&
             pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ))
        {
            do
            {
                nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
                ret = bExistsAltPath( pCG, pBNS, pBD, pAATG,
                                      at, num_atoms,
                                      cg_Minus /*nVertDoubleBond*/,
                                      cg_Plus /*nVertSingleBond*/,
                                      ALT_PATH_MODE_REM_PROTON );
                if (IS_BNS_ERROR( ret ))
                {
                    return ret;
                }
                if (ret & 1)
                {
                    nDelta = ( ret & ~3 ) >> 2;
                    /* djb-rwth: removing redundant code */
                    if (nDelta)
                    {
                        /* Radical pair has disappeared */
                        ; /* goto quick_exit;*/
                    }
                    if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])
                    {
                        nNumNeutralized += ( nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) / 2;
                    }
                }
            } while (ret & 1);
        }
    }

    ret = 0;
    if (tg_H_Acid >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Acid, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (tg_H_Other >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Other, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (cg_Minus >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (cg_Plus >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }

    pBNS->type_CN = 0;
    pBNS->type_T = 0;
    pBNS->type_TACN = 0;

    if (ret)
    {
        return ret;
    }

    if (pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] &&
         pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus])
    {
    }

    nPosCharges2 = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    nNegCharges2 = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    /*
    nNumNP_H2    = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] +
    pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton];
    nNumOS_H2    = pAATG->nAtTypeTotals[ATTOT_NUM_COH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_CSH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_ZOH];
    */
    if (( nPosCharges - nNegCharges ) - ( nPosCharges2 - nNegCharges2 ) != 0)
    {
        return BNS_PROGRAM_ERR;
    }

    if (nNumCanceledCharges)
    {
#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 )
        *nNumCanceledCharges += 2 * nNumNeutralized;
#else
        *nNumCanceledCharges = 2 * nNumNeutralized;
#endif
    }

    return nNumMoved2AcidH;
}


/****************************************************************************/
int HardAddAcidicProtons( CANON_GLOBALS *pCG,
                          inp_ATOM *at,
                          int num_atoms,
                          BN_AATG *pAATG,
                          int num2add,
                          int *nNumCanceledCharges,
                          BN_STRUCT *pBNS,
                          BN_DATA *pBD )
{
    int cg_Plus = 0;
    int cg_Minus_CO = 0;
    int cg_Minus_Other = 0;
    int tg_H = 0;

    int ret = 0, ret2;
    int nDelta, nNumChanges = 0, nNumMoved2AcidMinus = 0, nNumNeutralized = 0, nPrevNumCharges; /* djb-rwth: removing redundant variables */

    int nPosCharges, nPosCharges2;
    int nNegCharges, nNegCharges2;
    /*
    int nNumNP_H, nNumNP_H2;
    int nNumOS_H, nNumOS_H2;
    */
    nPosCharges = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    nNegCharges = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    /*
    nNumNP_H    =  pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] +
    pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton];
    nNumOS_H    =  pAATG->nAtTypeTotals[ATTOT_NUM_COH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_CSH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_ZOH];
    */

    /* Prevent free exchange H <-> (-) */
    pBNS->type_CN = ( BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE );
    pBNS->type_T = BNS_VERT_TYPE_TGROUP;
    pBNS->type_TACN = BNS_VERT_TYPE_ACID;

    /* Create (+) charge group */
    cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_POS, AA_HARD_MSK_POS, 1 );

    /* Create (-) charge group */
    /*
    if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] +
    nAtTypeTotals[ATTOT_NUM_CS_Minus] +
    nAtTypeTotals[ATTOT_NUM_ZO_Minus] +
    nAtTypeTotals[ATTOT_NUM_N_Minus] )
    */
    cg_Minus_CO = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_CO, AA_HARD_MSK_CO, -1 );

    cg_Minus_Other = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_NEG, AA_HARD_MSK_NEG, -1 );

    pBNS->type_CN = ( BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE );
    pBNS->type_T = BNS_VERT_TYPE_TGROUP;
    pBNS->type_TACN = BNS_VERT_TYPE_ACID;

    /* Create tautomeric group for all H  */
    tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_H, AA_HARD_MSK_H );

    if (cg_Minus_Other >= num_atoms && cg_Minus_CO >= num_atoms)
    {
        /* Find alt path to remove one proton */
        do
        {
            /* Add a proton */
            nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
            /* djb-rwth: addressing coverity ID #499474 -- cg_Minus_CO negative values handled properly */
            ret = bExistsAltPath( pCG, pBNS, pBD, pAATG,
                                  at, num_atoms,
                                  cg_Minus_Other /*nVertDoubleBond*/,
                                  cg_Minus_CO /*nVertSingleBond*/,
                                  ALT_PATH_MODE_REM_PROTON );
            if (IS_BNS_ERROR( ret ))
            {
                return ret;
            }
            if (ret & 1)
            {
                nDelta = ( ret & ~3 ) >> 2;
                nNumChanges += (0 != (ret & 2)); /* djb-rwth: ignoring LLVM warning: variable used */
                if (nDelta)
                {
                    /* Radical pair has disappeared */
                    ; /* goto quick_exit;*/
                }
                nNumMoved2AcidMinus++;
                if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1)
                {
                    nNumNeutralized += ( nPrevNumCharges - ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1 ) ) / 2;
                }
            }
        } while (( ret & 1 ) && nNumMoved2AcidMinus < num2add);

        /* Neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */
        if (( nNumMoved2AcidMinus /*|| bCancelChargesAlways*/ ) &&
             cg_Minus_Other >= num_atoms && cg_Plus >= num_atoms &&
             pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ))
        {
            do
            {
                nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
                ret = bExistsAltPath( pCG, pBNS, pBD, pAATG,
                                      at, num_atoms,
                                      cg_Minus_Other /*nVertDoubleBond*/,
                                      cg_Plus /*nVertSingleBond*/,
                                      ALT_PATH_MODE_REM_PROTON );
                if (IS_BNS_ERROR( ret ))
                {
                    return ret;
                }
                if (ret & 1)
                {
                    nDelta = ( ret & ~3 ) >> 2;
                    nNumChanges += (0 != (ret & 2)); /* djb-rwth: ignoring LLVM warning: variable used */
                    if (nDelta)
                    {
                        /* Radical pair has disappeared */
                        ; /* goto quick_exit;*/
                    }
                    if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])
                    {
                        nNumNeutralized += ( nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) / 2;
                    }
                }
            } while (ret & 1);
        }
    }

    ret = 0;
    if (tg_H >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (cg_Minus_Other >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_Other, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (cg_Minus_CO >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_CO, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (cg_Plus >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }

    pBNS->type_CN = 0;
    pBNS->type_T = 0;
    pBNS->type_TACN = 0;

    if (ret)
    {
        return ret;
    }

    if (pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] &&
         pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus])
    {
    }

    nPosCharges2 = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    nNegCharges2 = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    /*
    nNumNP_H2    = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] +
    pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton];
    nNumOS_H2    = pAATG->nAtTypeTotals[ATTOT_NUM_COH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_CSH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_ZOH];
    */

    if (( nPosCharges - nNegCharges ) - ( nPosCharges2 - nNegCharges2 ) != 0)
    {
        return BNS_PROGRAM_ERR;
    }

    if (nNumCanceledCharges)
    {
#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 )
        *nNumCanceledCharges += 2 * nNumNeutralized;
#else
        *nNumCanceledCharges = 2 * nNumNeutralized;
#endif
    }

    return nNumMoved2AcidMinus;
}


/****************************************************************************
HardRemoveHplusNP( ... )

Examples include removal of H from tautomeric O
that belongs to the same t-group as N:

>N(+)=-N=-OH =(taut.) =>
>N(+)=-NH-=O =(+charge move) =>
>N-=NH(+)-=O => >N-=N-=O + H(+)
****************************************************************************/
int HardRemoveHplusNP( CANON_GLOBALS *pCG,
                       inp_ATOM *at,
                       int num_atoms,
                       int bCancelChargesAlways,
                       int *nNumCanceledCharges,
                       BN_AATG *pAATG,
                       BN_STRUCT *pBNS,
                       BN_DATA *pBD )
{

    int cg_Plus = 0;
    int cg_Minus = 0;
    int tg_H = 0;
#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 )
    int cg_PlusP = 0;
#endif
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
    int nPrevRemovedProtons, nCurrRemovedProtons;
#endif
    int ret = 0, ret2;
    int nDelta, nNumChanges = 0, nNumRemovedProtons = 0, nNumNeutralized = 0, nPrevNumCharges; /* djb-rwth: ignoring LLVM warning: variable used */

    int nPosCharges, nPosCharges2;
    int nNegCharges, nNegCharges2;
    /*
    int nNumNP_H, nNumNP_H2;
    int nNumOS_H, nNumOS_H2;
    */

    nPosCharges = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    nNegCharges = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    /*
    nNumNP_H    =  pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] +
    pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton];
    nNumOS_H    =  pAATG->nAtTypeTotals[ATTOT_NUM_COH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_CSH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_ZOH];
    */

    /* Prevent free exchange H <-> (-) */
    pBNS->type_CN = ( BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE );
    pBNS->type_T = BNS_VERT_TYPE_TGROUP;
    pBNS->type_TACN = BNS_VERT_TYPE_ACID;

    /* Create (+) charge group */
    cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POS, PR_HARD_MSK_POS, 1 );

    /* Create (-) charge group */
    /*
    if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] +
    nAtTypeTotals[ATTOT_NUM_CS_Minus] +
    nAtTypeTotals[ATTOT_NUM_ZO_Minus] +
    nAtTypeTotals[ATTOT_NUM_N_Minus] )
    */
#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 )
    cg_PlusP = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POSP, PR_HARD_MSK_POS, 1 );
#endif
    cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 );

    /* Create single tautomeric group */
    tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_H, PR_HARD_MSK_H );

    if (tg_H >= num_atoms && cg_Plus >= num_atoms)
    {

#if ( FIX_N_MINUS_NORN_BUG == 1 )
        /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O; >N(+)=-NH(-) => >N-=NH */
        if (( nNumRemovedProtons || bCancelChargesAlways ) && cg_Minus >= num_atoms && cg_Plus >= num_atoms &&
             pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ))
        {
            do
            {
                nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
                nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
#endif
                ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, at, num_atoms,
                                      cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON );
                if (IS_BNS_ERROR( ret ))
                {
                    return ret;
                }
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
                nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
                if (nCurrRemovedProtons != nPrevRemovedProtons)
                {
                    return BNS_RADICAL_ERR;
                }
#endif
                if (ret & 1)
                {
                    nDelta = ( ret & ~3 ) >> 2;
                    nNumChanges += ( 0 != ( ret & 2 ) );
                    if (nDelta)
                    {
                        /* radical pair has disappeared */
                        ; /* goto quick_exit;*/
                    }
                    if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])
                    {
                        nNumNeutralized += ( nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) / 2;
                    }
                }
            } while (ret & 1);
        }
#endif

        /* Find alt path to remove one proton */
        do
        {
            /* Remove a proton */
            nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
            nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
#endif
            /* djb-rwth: addressing coverity ID #499572 -- tg_H negative values handled properly */
            ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, at, num_atoms,
                                  tg_H /*nVertDoubleBond*/,
                                  cg_Plus /*nVertSingleBond*/,
                                  ALT_PATH_MODE_REM_PROTON );
            if (IS_BNS_ERROR( ret ))
            {
                return ret;
            }
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
            nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
            if (nCurrRemovedProtons != nPrevRemovedProtons + ( ret & 1 ))
            {
                return BNS_RADICAL_ERR;
            }
#endif
            if (ret & 1)
            {
                nDelta = ( ret & ~3 ) >> 2;
                nNumChanges += (0 != (ret & 2)); /* djb-rwth: ignoring LLVM warning: variable used */
                if (nDelta)
                {
                    /* radical pair has disappeared */
                    ; /* goto quick_exit;*/
                }
                nNumRemovedProtons++;
                if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1)
                {
                    nNumNeutralized += ( nPrevNumCharges - ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1 ) ) / 2;
                }
            }
        } while (ret & 1);

        /* Neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */
        if (( nNumRemovedProtons || bCancelChargesAlways ) && cg_Minus >= num_atoms && cg_Plus >= num_atoms &&
             pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ))
        {
            do
            {
                nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
                nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
#endif
                ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, at, num_atoms,
                                      cg_Minus /*nVertDoubleBond*/,
                                      cg_Plus /*nVertSingleBond*/,
                                      ALT_PATH_MODE_REM_PROTON );
                if (IS_BNS_ERROR( ret ))
                {
                    return ret;
                }
#if ( FIX_REM_PROTON_COUNT_BUG == 1 )
                nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
                if (nCurrRemovedProtons != nPrevRemovedProtons)
                {
                    return BNS_RADICAL_ERR;
                }
#endif
                if (ret & 1)
                {
                    nDelta = ( ret & ~3 ) >> 2;
                    nNumChanges += (0 != (ret & 2)); /* djb-rwth: ignoring LLVM warning: variable used */
                    if (nDelta)
                    {
                        /* Radical pair has disappeared */
                        ; /* goto quick_exit;*/
                    }
                    if (nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])
                    {
                        nNumNeutralized += ( nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) / 2;
                    }
                }
            } while (ret & 1);
        }
    }

    ret = 0;
    if (tg_H >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
    if (cg_Minus >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 )
    if (cg_PlusP >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_PlusP, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }
#endif
    if (cg_Plus >= num_atoms)
    {
        ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS );
        if (!ret && ret2)
        {
            ret = ret2;
        }
    }

    pBNS->type_CN = 0;
    pBNS->type_T = 0;
    pBNS->type_TACN = 0;

    if (ret)
    {
        return ret;
    }

    if (pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] &&
         pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus])
    {
    }

    nPosCharges2 = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    nNegCharges2 = ( pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ) / 2;
    /*
    nNumNP_H2    = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] +
    pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton];
    nNumOS_H2    = pAATG->nAtTypeTotals[ATTOT_NUM_COH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_CSH] +
    pAATG->nAtTypeTotals[ATTOT_NUM_ZOH];
    */

    if (( nPosCharges - nNegCharges ) - ( nPosCharges2 - nNegCharges2 ) != nNumRemovedProtons)
    {
        return BNS_PROGRAM_ERR;
    }

    if (nNumCanceledCharges)
    {
#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 )
        *nNumCanceledCharges += 2 * nNumNeutralized;
#else
        *nNumCanceledCharges = 2 * nNumNeutralized;
#endif
    }

    return nNumRemovedProtons;
}


/****************************************************************************/
int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] )
{
    int i, max_num_ions, mask, type;
    /*int max_protons, max_O_Minus, num_H = 0, num_CO=0;*/

    if (nAtTypeTotals)
    {
        memset( nAtTypeTotals, 0, ATTOT_ARRAY_LEN * sizeof( nAtTypeTotals[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    }

    for (i = 0; i < num_atoms; i++)
    {
        type = GetAtomChargeType( atom, i, nAtTypeTotals, &mask, 0 );
        atom[i].at_type = type;
        /*
        num_H  += ((type & PR_HARD_TYP_H)  && (mask & ATBIT_MSK_H));
        num_CO += ((type & AR_HARD_TYP_HA) && (mask & AR_HARD_MSK_HA));
        */
    }

    if (nAtTypeTotals)
    {
        /*
        max_protons = nAtTypeTotals[ATTOT_NUM_NP_Proton] +
        inchi_min(num_H, nAtTypeTotals[ATTOT_NUM_NP_Plus]);
        max_O_Minus = nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] +
        nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_OO_Minus] +
        nAtTypeTotals[ATTOT_NUM_ZOO_Minus] + nAtTypeTotals[ATTOT_NUM_NO_Minus] +
        nAtTypeTotals[ATTOT_NUM_O_Minus] +nAtTypeTotals[ATTOT_NUM_N_Minus];
        ;
        max_num_ions = max_protons + max_O_Minus + nAtTypeTotals[ATTOT_NUM_CHARGES];
        */
        max_num_ions = nAtTypeTotals[ATTOT_NUM_CHARGES];
    }
    else
    {
        max_num_ions = 0;
    }

    return max_num_ions;
}


/****************************************************************************/
int RemoveNPProtonsAndAcidCharges( CANON_GLOBALS *pCG,
                                   inp_ATOM *at,
                                   int num_atoms,
                                   BN_AATG *pAATG,
                                   BN_STRUCT *pBNS,
                                   BN_DATA *pBD )
{

    /* Prepare data structure */
    int num;
    int nNumCanceledCharges = 0;
    /* djb-rwth: removing redundant variables */
    T_GROUP_INFO *t_group_info = pAATG->t_group_info;
    int ret = 0, bError = 0;

    int bAllowHardRemove = ( t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__SALTS ) &&
        ( t_group_info->bTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) &&
        ( t_group_info->bTautFlags & TG_FLAG_MOVE_POS_CHARGES ) &&
        ( t_group_info->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS );

    if (pAATG->nMarkedAtom && num_atoms < pAATG->nAllocLen)
    {
        inchi_free( pAATG->nMarkedAtom );
        qzfree( pAATG->nEndPoint );
        memset( pAATG, 0, sizeof( *pAATG ) ); /* djb-rwth: memset_s C11/Annex K variant? */
    }

    if (!pAATG->nMarkedAtom && ( pAATG->nMarkedAtom = (S_CHAR *) inchi_malloc( num_atoms * sizeof( pAATG->nMarkedAtom[0] ) ) ))
    {
        pAATG->nAllocLen = num_atoms;
        pAATG->nNumFound = 0;
    }

    /* o TECHMAN-5.1. Remove protons from charged heteroatoms */

    /* (TECHMAN-5.1a) Simple remove of protons from N, P, and O,S,Se,Te */
    if ((num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton] + pAATG->nAtTypeTotals[ATTOT_NUM_OH_Plus])) /* djb-rwth: addressing LLVM warning */
    {
        ret = SimpleRemoveHplusNPO( at, num_atoms, pAATG->nAtTypeTotals, t_group_info );
        if (ret != num)
        {
            bError = BNS_PROGRAM_ERR;
            goto exit_function;
        }
        /*t_group_info->nNumRemovedProtons  += ret;*/
        t_group_info->tni.bNormalizationFlags |= ( ret > 0 ) ? FLAG_PROTON_NPO_SIMPLE_REMOVED : 0;
    }

    if (( num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Plus] ) && bAllowHardRemove) /* djb-rwth: ignoring LLVM warning: variable used */
    {
        /* [TECHMAN-5.1b] Hard removing more protons from cationic N; charges may be canceled */
        ret = HardRemoveHplusNP( pCG, at, num_atoms, 1, &nNumCanceledCharges, pAATG, pBNS, pBD );
        if (IS_BNS_ERROR( ret ))
        {
            bError = ret;
            goto exit_function;
        }
        /* djb-rwth: removing redundant code */
        /*t_group_info->nNumRemovedProtons  += ret;*/
        t_group_info->tni.bNormalizationFlags |= ( ret > 0 ) ? FLAG_PROTON_NP_HARD_REMOVED : 0;
    }

    if (pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0)
    {
        /* o TECHMAN-5.2. Remove protons from neutral heteroatoms */

        /* (TECHMAN-5.2a) Simple removal */
        ret = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] );
        if (IS_BNS_ERROR( ret ))
        {
            bError = ret;
            goto exit_function;
        }

        /*t_group_info->nNumRemovedProtons  += ret;*/
        t_group_info->tni.bNormalizationFlags |= ( ret > 0 ) ? FLAG_PROTON_AC_SIMPLE_REMOVED : 0;

        
        if (pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 && bAllowHardRemove)
        {
            /* (TECHMAN-5.2b) Hard removal */
            ret = HardRemoveAcidicProtons(pCG, at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD);
            if (IS_BNS_ERROR(ret))
            {
                bError = ret;
                goto exit_function;
            }
            if (ret > 0)
            {
                int ret2 = SimpleRemoveAcidicProtons(at, num_atoms, pAATG, ret);
                if (ret2 != ret)
                {
                    bError = BNS_PROGRAM_ERR;
                    goto exit_function;
                }
                /*t_group_info->nNumRemovedProtons  += ret;*/
                t_group_info->tni.bNormalizationFlags |= (ret > 0) ? FLAG_PROTON_AC_HARD_REMOVED : 0;
                /* djb-rwth: removing redundant code */
            }
        }


    }
    else
    {
        if (pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0)
        {
            ret = SimpleAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] );
            if (IS_BNS_ERROR( ret ))
            {
                bError = ret;
                goto exit_function;
            }
            /*t_group_info->nNumRemovedProtons  -= ret;*/
            /*
            CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N
            along an alternating path from another heteroatom (t-group will be detected).
            */
            t_group_info->tni.bNormalizationFlags |= ( ret > 0 ) ? FLAG_PROTON_AC_SIMPLE_ADDED : 0;
            if (pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 && bAllowHardRemove)
            {
                ret = HardAddAcidicProtons( pCG, at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD );
                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                    goto exit_function;
                }
                if (ret > 0)
                {
                    int ret2 = SimpleAddAcidicProtons( at, num_atoms, pAATG, ret );
                    if (ret2 != ret)
                    {
                        bError = BNS_PROGRAM_ERR;
                        goto exit_function;
                    }
                    /*t_group_info->nNumRemovedProtons  -= ret;*/
                    t_group_info->tni.bNormalizationFlags |= FLAG_PROTON_AC_HARD_ADDED; /* djb-rwth: fixing coverity ID #499527 */
                    /* djb-rwth: removing redundant code */
                }
            }
        }
    }

    t_group_info->tni.bNormalizationFlags |= nNumCanceledCharges ? FLAG_PROTON_CHARGE_CANCEL : 0;

exit_function:
    if (bError)
    {
        ret = IS_BNS_ERROR( bError ) ? bError : BNS_PROGRAM_ERR;
    }

    return ret;
}


/****************************************************************************
Main normalization procedure
****************************************************************************/
int mark_alt_bonds_and_taut_groups( struct tagINCHI_CLOCK   *ic,
                                    struct tagCANON_GLOBALS *pCG,
                                    inp_ATOM                *at,
                                    inp_ATOM                *at_fixed_bonds_out,
                                    int                     num_atoms,
                                    struct tagInchiTime     *ulTimeOutTime,
                                    T_GROUP_INFO            *t_group_info,
                                    INCHI_MODE              *inpbTautFlags,
                                    INCHI_MODE              *inpbTautFlagsDone,
                                    int                     nebend,
                                    int                     *ebend )


{
    BN_STRUCT *pBNS = NULL;
    BN_DATA   *pBD = NULL;
    int bError, nChanges, taut_found, salt_found, salt_pass, salt_step, ret, ret2, num, num_changed_bonds; /* djb-rwth: removing redundant variables */
    int max_altp = BN_MAX_ALTP;
    int bChangeFlow = ( BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS );
    BNS_FLOW_CHANGES fcd[BNS_MAX_NUM_FLOW_CHANGES + 1];
    C_GROUP_INFO CGroupInfo;
    C_GROUP_INFO *c_group_info = &CGroupInfo;
    S_GROUP_INFO SGroupInfo;
    S_GROUP_INFO *s_group_info = &SGroupInfo;
    INCHI_MODE    *pbTautFlags = t_group_info ? &t_group_info->bTautFlags : inpbTautFlags;
    INCHI_MODE    *pbTautFlagsDone = t_group_info ? &t_group_info->bTautFlagsDone : inpbTautFlagsDone;

    int nAtTypeTotals[ATTOT_ARRAY_LEN];
    int nNumOrigTotAtoms;

    BN_AATG  aatg;
    BN_AATG *pAATG = &aatg;

#ifdef FIX_AROM_RADICAL        /* Added 2011-05-09 IPl */
    int i, n_arom_radicals = 0, *stored_radicals = NULL;
#endif
    int at_prot;  /* moved from below 2024-09-01 DT */

    nChanges = 0;
    bError = 0;

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

    /*(@nnuk : Nauman Ullah Khan) :: Variable for checking (De)protonation status */
    LOG_NO_ARGS("\n############# Initial state before (De)Protonation (L5373:ichi_bns.c) ###############\n");
    for (at_prot = 0; at_prot < num_atoms; at_prot++)
    {
        LOG_MULT_ARGS("Atom %d: Element: %s, Num_H: %d, Charge: %hhd, Radical: %d\n", at_prot, at[at_prot].elname, at[at_prot].num_H, at[at_prot].charge, at[at_prot].radical);
    }
    LOG_NO_ARGS("\n#####################################################################################\n");


#ifdef FIX_AROM_RADICAL        /* Added 2011-05-09 IPl */
    for (i = 0; i < num_atoms; i++)
    {
        if (( at[i].radical == RADICAL_DOUBLET ) && ( at[i].valence == 2 ) &&
            ( at[i].bond_type[0] == BOND_ALTERN ) && ( at[i].bond_type[1] == BOND_ALTERN ))
        {
            n_arom_radicals++;
            if (!stored_radicals)
            {
                stored_radicals = (int *) inchi_calloc( num_atoms, sizeof( int ) );
                /* 2011-08-05 explicit cast added due to Evan Bolton */
                if (!stored_radicals)
                {
                    bError = BNS_OUT_OF_RAM;
                    goto exit_function;
                }
                stored_radicals[i] = RADICAL_DOUBLET;
                at[i].radical = 0;
                at[i].num_H++;
            }
        }
    }

#endif

    if (( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) && num_atoms > 1)
    {
        /* Charge groups memory allocation */
        c_group_info->c_group = (C_GROUP *) inchi_calloc( num_atoms / 2, sizeof( c_group_info->c_group[0] ) );
        c_group_info->c_candidate = (C_CANDIDATE*) inchi_calloc( num_atoms, sizeof( c_group_info->c_candidate[0] ) );
        if (c_group_info->c_group && c_group_info->c_candidate)
        {
            c_group_info->max_num_c_groups = num_atoms / 2;
            c_group_info->max_num_candidates = num_atoms;
        }
        else
        {
            bError = BNS_OUT_OF_RAM; /* error: out of RAM */
                                     /*printf("BNS_OUT_OF_RAM-1: num_at=%d, c_gr=%lx c_can=%lx\n", num_atoms, c_group_info->c_group, c_group_info->c_candidate);*/
            goto exit_function;
        }
    }

    if (*pbTautFlags & TG_FLAG_TEST_TAUT__SALTS)
    {
        if (t_group_info)
        {
            /* Salt groups memory allocation */
            s_group_info->s_candidate =
                (S_CANDIDATE*) inchi_calloc( num_atoms,
                                             sizeof( s_group_info->s_candidate[0] ) );
            if (s_group_info->s_candidate)
            {
                s_group_info->max_num_candidates = num_atoms;
            }
            else
            {
                bError = BNS_OUT_OF_RAM; /* error: out of RAM */
                                         /*printf("BNS_OUT_OF_RAM-2\n");*/
                goto exit_function;
            }
        }
    }

    if (t_group_info)
    {
        if (t_group_info->tGroupNumber)
        {
            inchi_free( t_group_info->tGroupNumber );
        }
        t_group_info->tGroupNumber = (AT_NUMB *) inchi_calloc( 2 * (long long)num_atoms + 1, sizeof( t_group_info->tGroupNumber[0] ) ); /* djb-rwth: cast operator added */
        if (!t_group_info->tGroupNumber)
        {
            /*printf("BNS_OUT_OF_RAM-9\n");*/
            bError = BNS_OUT_OF_RAM; /* error: out of RAM */
            goto exit_function;
        }
        num = t_group_info->tni.nNumRemovedExplicitH;
        memset( &t_group_info->tni, 0, sizeof( t_group_info->tni ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        t_group_info->tni.nNumRemovedExplicitH = num;
    }

    /*
    again:
    */

    /* Allocate Balanced Network Data Strucures; replace Alternating bonds with Single */
    if (( pBNS = AllocateAndInitBnStruct( at, num_atoms,
                                          BNS_ADD_ATOMS, BNS_ADD_EDGES,
                                          max_altp, &num_changed_bonds ) )
         &&
         ( pBD = AllocateAndInitBnData( pBNS->max_vertices ) ))
    {


        pBNS->pbTautFlags = pbTautFlags;     /* carry through all functions */
        pBNS->pbTautFlagsDone = pbTautFlagsDone;  /* carry through all functions */

        pBNS->ulTimeOutTime = ulTimeOutTime;    /* v. 1.05 */
        pBNS->ic = ic;    /* v. 1.05 */

#if ( BNS_PROTECT_FROM_TAUT == 1 )
                          /* Protect bonds to acetyl and nitro */
        SetForbiddenEdges( pBNS, at, num_atoms, BNS_EDGE_FORBIDDEN_MASK, nebend, ebend );
#endif

        /* Set bonds in case of input "aromatic" bonds or multiple radicals */
#ifdef FIX_AROM_RADICAL        /* Added 2011-05-09 IPl */
        if (n_arom_radicals)
        {
            ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );
            if (stored_radicals)
            {
                for (i = 0; i < num_atoms; i++)
                {
                    if (stored_radicals[i])
                    {
                        at[i].radical = stored_radicals[i];
                        at[i].num_H--;
                    }
                }
            }
            if (IS_BNS_ERROR( ret ))
            {
                bError = ret;
                goto exit_function;
            }
        }
#endif

        ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );

        /* (here pair(s) of radicals could have disappeared from the atoms) */
        if (IS_BNS_ERROR( ret ))
        {
            bError = ret;
            goto exit_function;
        }
        pBNS->tot_st_flow += 2 * ret;

        /*return 0;*/ /* debug */
        /* djb-rwth: removing redundant code */

        if (pBNS->tot_st_cap > pBNS->tot_st_flow)
        {
            /* has radical */
            bChangeFlow |= BNS_EF_SET_NOSTEREO;
        }

        /********************************************************************
        *  Remove protons from NH(+), but not PH(+)
        *  Add protons to COO(-) etc.
        *  or remove protons from COOH etc to make the organic part neutral
        *  Note: for now (-) from N(-) can be only canceled or moved to -C=O
        ********************************************************************/
        if (( *pbTautFlags & TG_FLAG_VARIABLE_PROTONS ) &&
             t_group_info &&
             mark_at_type( at, num_atoms, nAtTypeTotals ) &&
             nAtTypeTotals[ATTOT_NUM_CHARGES])
        {
            /*
            the structure is simple to neutralize if it yields exactly
            num[H(+)]     = num[N,P H(+)]
            num[N,S,O(-)] = num[=C-O(-)] + num[C-S(-)] + num[N(-)] + num[other O(-), S(-)]

            and n(p) = num[H(+)] - num[N,S,O(-)] (no protons, no negative N,O,S condition)

            Additional check is needed if:
            min{num[N,PH], num[N,P(+), not onium]} > 0
            => possibility to yield more H(+)

            min_charge = orig_charge(P,N,O,S) - n(p) - n(OH,SH)
            max_charge = orig_charge(P,N,O,S) - n(p) + n(O,S,N(-))
            */

            nNumOrigTotAtoms = t_group_info->tni.nNumRemovedExplicitH + num_atoms;
            pAATG->nAtTypeTotals = nAtTypeTotals;
            pAATG->t_group_info = t_group_info;
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
            pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
#endif

            /***********************************************************/
            /*                                                         */
            /*          ( D E ) P R O T O N A T I O N                  */
            /*                                                         */
            /***********************************************************/

            ret = RemoveNPProtonsAndAcidCharges( pCG, at, num_atoms, pAATG, pBNS, pBD );

#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
            pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
#endif
            if (IS_BNS_ERROR( ret ))
            {
                bError = ret;
                goto exit_function;
            }

            if (t_group_info->tni.bNormalizationFlags)
            {
                SetInitCapFlowToCurrent( pBNS );
                if (at_fixed_bonds_out)
                {
                    /* Copy modified initial tautomeric structure for displaying
                    Warning: implicit H counts in at_fixed_bonds_out include explicit Hs */

                    memcpy(at_fixed_bonds_out, at, nNumOrigTotAtoms * sizeof(at_fixed_bonds_out[0]));

                    /* -- will be done in FillOutInputInfAtom() --
                    RemoveExcessiveImplicitH( num_atoms, t_group_info->tni.nNumRemovedExplicitH, at_fixed_bonds_out );
                    */
                }
            }
        }

        /****************** Initial bonds normalization ***************/

        if (*pbTautFlags & TG_FLAG_MOVE_POS_CHARGES)
        {
            /******************* Find moveable positive charges **********************/
            do
            {
                /* Cycling while ret>0 added 2004-06-04 */
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
                CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms );
#endif
                ret = MarkChargeGroups( pCG, at, num_atoms,
                                        c_group_info, t_group_info,
                                        pBNS, pBD );

#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
#endif
                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                    goto exit_function;
                }
                if (ret)
                {
                    nChanges += ret;
                    ret2 = AddCGroups2BnStruct( pCG, pBNS, at, num_atoms, c_group_info );
                    if (IS_BNS_ERROR( ret2 ))
                    {
                        bError = ret2;
                        goto exit_function;
                    }
                    *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE;
                }
            } while (ret > 0);


#if ( BNS_RAD_SEARCH == 1 )
#else
            /* moveable charges may allow to cancel radicals -- check it */
            if (pBNS->tot_st_cap > pBNS->tot_st_flow)
            {
                ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );
                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                    goto exit_function;
                }
                if (ret > 0)
                {
                    /*
                    pBNS->tot_st_flow += 2*ret;
                    ret = ReInitBnStruct( pBNS, at, num_atoms, 1 );
                    if ( IS_BNS_ERROR( ret ) ) {
                    bError = ret;
                    goto exit_function;
                    }
                    */
                    bError = BNS_RADICAL_ERR;
                    goto exit_function;
                }
            }
#endif
        }

        /************************************************************************/
        /********          Test bonds for bond tautomerism         **************/
        /******** replace moveable bonds with "alternating" bonds  **************/
        /************************************************************************/
        ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 );

        if (IS_BNS_ERROR( ret ))
        {
            bError = ret;
            goto exit_function;
        }
        /* djb-rwth: removing redundant code */

        /*********************** End of initial bonds normalization *************/

        /* djb-rwth: removing redundant code */

        /* Check for tautomerism */
        /* find new tautomer groups */
        salt_pass = 0;
        salt_step = 0;
        salt_found = 0;

        /*************************************************************/
        /*                                                           */
        /*           M A I N   C Y C L E   B E G I N                 */
        /*                                                           */
        /*************************************************************/

        do
        {
            /* djb-rwth: removing redundant code */
            nChanges = 0;
            /* djb-rwth: removing redundant code */

            /**************** Regular bond/H/(-)/positive charges tautomerism cycle begin **************/
            do
            {
                /* djb-rwth: removing redundant code */
                for (taut_found = 0;
                      0 < ( ret = MarkTautomerGroups( pCG, at, num_atoms,
                                                      t_group_info, c_group_info,
                                                      pBNS, pBD ) );
                      taut_found++)
                {
                    ;
                }
                if (ret < 0)
                {
                    bError = ret;
                }
                if (taut_found && !salt_pass)
                {
                    *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__ATOMS_DONE;
                }

                if (taut_found || salt_found)
                {
                    /****************** repeat bonds normalization ***************/
                    ret = ReInitBnStructAddGroups( pCG, pBNS, at, num_atoms,
                                                   t_group_info, c_group_info );
                    if (IS_BNS_ERROR( ret ))
                    {
                        bError = ret;
                        goto exit_function;
                    }
#if ( BNS_RAD_SEARCH == 1 )
#else
                    /* discovered moveable charges and H-atoms may allow to cancel radicals */
                    if (pBNS->tot_st_cap > pBNS->tot_st_flow)
                    {
                        ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );
                        if (IS_BNS_ERROR( ret ))
                        {
                            bError = ret;
                            goto exit_function;
                        }
                        if (ret > 0)
                        {
                            /*
                            pBNS->tot_st_flow += 2*ret;
                            ret = ReInitBnStruct( pBNS, at, num_atoms, 1 );
                            if ( IS_BNS_ERROR( ret ) ) {
                            bError = ret;
                            goto exit_function;
                            }
                            */
                            bError = BNS_RADICAL_ERR;
                            goto exit_function;
                        }
                    }
#endif
                    /****************** Update bonds normalization ***************/
                    if (*pbTautFlags & TG_FLAG_MOVE_POS_CHARGES)
                    {
                        /******************* Find moveable charges ***************/
                        do
                        {
                            /* Cycling while ret>0 added 2004-06-04 */
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                            pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
                            CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms );
#endif

                            ret = MarkChargeGroups( pCG, at, num_atoms,
                                                    c_group_info, t_group_info,
                                                    pBNS, pBD );
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                            pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
#endif
                            if (IS_BNS_ERROR( ret ))
                            {
                                bError = ret;
                                goto exit_function;
                            }
                            nChanges += ret;
                            if (ret > 0)
                            {
                                ret2 = ReInitBnStructAddGroups( pCG, pBNS,
                                                                at, num_atoms,
                                                                t_group_info,
                                                                c_group_info );
                                if (IS_BNS_ERROR( ret2 ))
                                {
                                    bError = ret2;
                                    goto exit_function;
                                }
                                *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE;
                            }
                        } while (ret > 0);
                    }

                    /************************************************************************/
                    /********             Find moveable bonds:                 **************/
                    /********          test bonds for bond tautomerism         **************/
                    /******** replace moveable bonds with "alternating" bonds  **************/
                    /************************************************************************/
                    ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 );
                    if (IS_BNS_ERROR( ret ))
                    {
                        bError = ret;
                        goto exit_function;
                    }
                    nChanges += ret;
                    /****************** End of update bonds normalization ***************/

                }
                salt_found = 0;
            } while (taut_found && !bError);


            /**************** Regular bond/H/(-)/positive charges tautomerism cycle end **************/

            if (bError)
            {
                break;
            }


            /******************* 'Salt' tautomerism permitted *************************/
            if (*pbTautFlags & TG_FLAG_TEST_TAUT__SALTS)
            {

                if (*pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS)
                {
                    /*********** Requested one or more "salt" attachement migrartion test ********/
                    if (!nChanges && salt_pass && salt_step)
                    {
                        break;  /* done */
                    }
                    if (!salt_step)
                    {
                        /* Salt step 0: process one attachment migrartion */
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                        pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
                        CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms );
#endif
                        salt_found = MarkSaltChargeGroups( pCG, at, num_atoms,
                                                           s_group_info,
                                                           t_group_info,
                                                           c_group_info,
                                                           pBNS, pBD );
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                        pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
#endif
                        if (salt_found < 0)
                        {
                            bError = salt_found;
                            break;
                        }
                        else
                        {
                            if (salt_found > 0)
                            {
                                *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE;
                            }
                        }
                        salt_step = !salt_found;
                        /* if new 'salt' atoms have been found then repeat regular taut. search
                        *      MarkTautomerGroups() and do not perform salt step 1
                        * if new 'salt' atoms have NOT been found then switch to salt step 1
                        *      and never repeat salt step 0 for the current structure
                        */
                    }

                    if (salt_step /*||
                                  (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT)*/)
                    {
                        /* Salt step 1: process more than one attachment migration */
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                        pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
                        CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms );
#endif
                        salt_found = MarkSaltChargeGroups2( pCG, at, num_atoms,
                                                            s_group_info,
                                                            t_group_info,
                                                            c_group_info,
                                                            pBNS, pBD );
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                        pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
#endif
                        if (salt_found < 0)
                        {
                            bError = salt_found;
                            break;
                        }
                        else
                        {
                            if (salt_found == 1 || salt_found == 5)
                            {
                                *pbTautFlagsDone |= TG_FLAG_TEST_TAUT2_SALTS_DONE;
                                if (salt_found == 5)
                                {
                                    *pbTautFlagsDone |= TG_FLAG_TEST_TAUT3_SALTS_DONE;
                                }
                                /* salt_found == 2 => only negative charges involved */
                            }
                        }
                    }

                    salt_pass++;
                }
                else
                {
                    /* !( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */
                    /*************** Requested only one attachement migration test **********/
                    if (!nChanges && salt_pass)
                    {
                        /* One attachment migration */
                        break;
                    }
                    /* Salt step 0: process one attachment migration */
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                    pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
                    CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms );
#endif
                    salt_found = MarkSaltChargeGroups( pCG, at, num_atoms,
                                                       s_group_info,
                                                       t_group_info,
                                                       c_group_info,
                                                       pBNS, pBD );
#if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
                    pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
#endif
                    if (salt_found < 0)
                    {
                        bError = salt_found;
                        break;
                    }
                    else
                    {
                        if (salt_found > 0)
                        {
                            *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE;
                        }
                    }
                    salt_pass++;
                } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */
            } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) */
        } while (salt_found && !bError);

        /*************************************************************/
        /*                                                           */
        /*           M A I N   C Y C L E   E N D                     */
        /*                                                           */
        /*************************************************************/


        if (*pbTautFlags & TG_FLAG_MERGE_TAUT_SALTS)
        {
            if (!bError && s_group_info /*&& s_group_info->num_candidates > 0*/)
            {
                ret = MergeSaltTautGroups( pCG, at, num_atoms, s_group_info,
                                           t_group_info, c_group_info, pBNS );
                if (ret < 0)
                {
                    bError = ret;
                }
                else
                {
                    if (ret > 0)
                    {
                        *pbTautFlagsDone |= TG_FLAG_MERGE_TAUT_SALTS_DONE;
                    }
                }
            }
        }

        if (!bError && t_group_info &&
            ( t_group_info->bTautFlags & TG_FLAG_VARIABLE_PROTONS ) &&
             ( t_group_info->bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE | TG_FLAG_FOUND_ISOTOPIC_H_DONE ) ))
        {
            ret = MakeIsotopicHGroup( at, num_atoms, s_group_info, t_group_info );
            if (ret < 0)
            {
                bError = ret;
            }
        }

        /* Success */

        remove_alt_bond_marks( at, num_atoms );

        /************************************************
        *  Temporarily ignore all non-alternating bonds
        *  and mark non-ring alt bonds non-stereogenic
        ************************************************/

        ReInitBnStructForAltBns( pBNS, at, num_atoms, 0 );
        MarkRingSystemsAltBns( pBNS, 0 );
        MarkNonStereoAltBns( pBNS, at, num_atoms, 0 );
#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 )
        /* second time unknown ("Either") alternating bonds are treated as non-stereogenic */
        /* stereobonds bonds that lost stereo get "Either" stereo_type */
        ReInitBnStructForAltBns( pBNS, at, num_atoms, 1 );
        MarkRingSystemsAltBns( pBNS, 1 );
        MarkNonStereoAltBns( pBNS, at, num_atoms, 1 );
#endif
    }
    else
    {
        bError = BNS_OUT_OF_RAM;
        /*printf("BNS_OUT_OF_RAM-3\n");*/
    }

    /*(@nnuk : Nauman Ullah Khan) */
    LOG_NO_ARGS("\n################# Modified state after (De)Protonation (L6014:ichi_bns.c) ################\n");
    for (at_prot = 0; at_prot < num_atoms; at_prot++)
    {
        LOG_MULT_ARGS("Atom %d: Element: %s, Num_H: %d, Charge: %hhd, Radical: %d\n", at_prot, at[at_prot].elname, at[at_prot].num_H, at[at_prot].charge, at[at_prot].radical);
    }
    LOG_NO_ARGS("\n##########################################################################################\n");

exit_function:

    /* djb-rwth: ignoring LLVM warning: variables used to store functions return values */
    pBNS = DeAllocateBnStruct(pBNS);
    pBD = DeAllocateBnData(pBD);
    /*#if ( MOVE_CHARGES == 1 )*/
    if (c_group_info)
    {
        if (c_group_info->c_group)
        {
            inchi_free( c_group_info->c_group );
        }
        if (c_group_info->c_candidate)
        {
            inchi_free( c_group_info->c_candidate );
        }
    }
    /*#endif*/
    if (s_group_info && s_group_info->s_candidate)
    {
        inchi_free( s_group_info->s_candidate );
    }
    if (pAATG && pAATG->nMarkedAtom)
    {
        inchi_free( pAATG->nMarkedAtom );
        qzfree( pAATG->nEndPoint );
        /*qzfree( pAATG->nAtTypeTotals );*/ /* nAtTypeTotals is a stack array */
    }
    if (t_group_info && t_group_info->tGroupNumber)
    {
        inchi_free( t_group_info->tGroupNumber );
        t_group_info->tGroupNumber = NULL;
    }

    if (!bError && num_atoms == 1 && at[0].at_type == ATT_PROTON && t_group_info && !t_group_info->tni.nNumRemovedExplicitH)
    {
        /* remove single isolated proton */
        t_group_info->tni.nNumRemovedProtons = 1;
        t_group_info->tni.bNormalizationFlags |= FLAG_PROTON_SINGLE_REMOVED;
        if (at[0].iso_atw_diff) 
        {
#if ( FIX_CURE53_ISSUE_NULL_DEREFERENCE_MAKE_A_COPY_OF_T_GROUP_INFO==1 || defined(FIX_IMPOSSIBLE_H_ISOTOPE_BUG) )
            if (at[0].iso_atw_diff <= NUM_H_ISOTOPES)
            {
                /* djb-rwth: possible false positive oss-fuzz issue #39064660 */
                t_group_info->tni.nNumRemovedProtonsIsotopic[at[0].iso_atw_diff - 1] ++;
            }
#else
            t_group_info->tni.nNumRemovedProtonsIsotopic[at[0].iso_atw_diff - 1] ++;
#endif
        }
        if (at_fixed_bonds_out)
        {
            memcpy(at_fixed_bonds_out, at, num_atoms * sizeof(at_fixed_bonds_out[0]));
        }
        /*num_atoms --;*/
    }


    /*
    Additional currently unused info:

    nOrigDelta > 0: original structure has been changed
    due to fiund augmenting path(s)
    nChanges   > 0: either alt. bonds or taut. groups have been found
    */

#ifdef FIX_AROM_RADICAL        /* Added 2011-05-09 IPl */
    if (stored_radicals)
    {
        inchi_free( stored_radicals );
    }
#endif

    return bError ? bError : num_atoms;  /* ret = 0 => success, any other => error */
}


/****************************************************************************/
int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge )
{
    BNS_EDGE *pEdge = pBNS->edge + iedge;
    int       nMaxFlow = ( pEdge->cap & EDGE_FLOW_MASK ); /* edge cap */

    if (nMaxFlow > MAX_BOND_EDGE_CAP)
    {
        nMaxFlow = MAX_BOND_EDGE_CAP;
    }
    return nMaxFlow;
}


/****************************************************************************/
int nCurFlow2Check( BN_STRUCT *pBNS, int iedge )
{
    BNS_EDGE *pEdge = pBNS->edge + iedge;
    int       nCurFlow = ( pEdge->flow & EDGE_FLOW_MASK ); /* edge flow */
    return    nCurFlow;
}


/****************************************************************************/
int nMinFlow2Check( BN_STRUCT *pBNS, int iedge )
{
    BNS_EDGE *pEdge = pBNS->edge + iedge;
    Vertex    v1 = pEdge->neighbor1;
    Vertex    v2 = v1 ^ pEdge->neighbor12;
    int       f12 = ( pEdge->flow & EDGE_FLOW_MASK );
    int       rescap1, rescap2, rescap12, i, iedge_i;

    if (f12 > 0)
    {
        for (i = 0, rescap1 = 0; i < pBNS->vert[v1].num_adj_edges; i++)
        {
            iedge_i = pBNS->vert[v1].iedge[i];
            if (iedge_i == iedge)
            {
                continue;
            }
            rescap1 += ( pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK ) - ( pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK );
        }
        for (i = 0, rescap2 = 0; i < pBNS->vert[v2].num_adj_edges; i++)
        {
            iedge_i = pBNS->vert[v2].iedge[i];
            if (iedge_i == iedge)
            {
                continue;
            }
            rescap2 += ( pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK ) - ( pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK );
        }
        rescap12 = inchi_min( rescap1, rescap2 );
        rescap12 = inchi_min( rescap12, f12 );
        return f12 - rescap12;
    }

    return 0;
}


/****************************************************************************/
int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS,
                                BNS_FLOW_CHANGES *fcd,
                                int nTestFlow,
                                inp_ATOM *at,
                                int num_atoms,
                                int bChangeFlow0 )
{
    int    ifcd, iedge, new_flow, ret_val, nChanges = 0, bError = 0;
    int    bChangeFlow;
    Vertex v1, v2;
    int    ineigh1, ineigh2;
    BNS_EDGE *pEdge;

    bChangeFlow0 &= ~BNS_EF_CHNG_RSTR;  /* do not change pEdge flow in SetBondType */
    if (!bChangeFlow0)
    {
        return 0;
    }

    bChangeFlow = ( bChangeFlow0 & ~BNS_EF_SET_NOSTEREO );

    /* Find the next to the last changed */
    if (bChangeFlow0 & BNS_EF_SET_NOSTEREO)
    {
        for (ifcd = 0; NO_VERTEX != ( iedge = fcd[ifcd].iedge ); ifcd++) /* djb-rwth: ignoring LLVM warning: variable used */
        {
            iedge = fcd[ifcd].iedge;
            pEdge = pBNS->edge + iedge;
            if (!pEdge->pass)
            {
                continue;
            }

            if (!ifcd && nTestFlow >= 0)
            {
                new_flow = nTestFlow;
            }
            else
            {
                new_flow = (int) pEdge->flow;
            }

            v1 = pEdge->neighbor1;
            v2 = pEdge->neighbor12 ^ v1;
            if (v1 < num_atoms && v2 < num_atoms && new_flow != pEdge->flow0)
            {
                if (( pBNS->vert[v1].st_edge.cap0 == pBNS->vert[v1].st_edge.flow0 ) !=
                    ( pBNS->vert[v1].st_edge.cap == pBNS->vert[v1].st_edge.flow ) ||
                     ( pBNS->vert[v2].st_edge.cap0 == pBNS->vert[v2].st_edge.flow0 ) !=
                     ( pBNS->vert[v2].st_edge.cap == pBNS->vert[v2].st_edge.flow ))
                {
                    bChangeFlow |= BNS_EF_SET_NOSTEREO;
                    nChanges |= BNS_EF_SET_NOSTEREO;
                }
            }
        }
    }
    else
    {
        for (ifcd = 0; NO_VERTEX != ( iedge = fcd[ifcd].iedge ); ifcd++) /* djb-rwth: ignoring LLVM warning: variable used */
        {
            ;
        }
    }

    /* restore in reversed order to correctly handle vertex changed more than once */
    for (ifcd -= 1; 0 <= ifcd; ifcd--)
    {

        iedge = fcd[ifcd].iedge;
        pEdge = pBNS->edge + iedge;
        if (!pEdge->pass)
        {
            continue;
        }

        if (!ifcd && nTestFlow >= 0)
        {
            new_flow = nTestFlow;
        }
        else
        {
            new_flow = (int) pEdge->flow;
        }

        v1 = pEdge->neighbor1;
        v2 = pEdge->neighbor12 ^ v1;
        if (v1 < num_atoms && v2 < num_atoms && bChangeFlow && new_flow != pEdge->flow0)
        {
            ineigh1 = pEdge->neigh_ord[0];
            ineigh2 = pEdge->neigh_ord[1];
            ret_val = SetAtomBondType( pEdge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], new_flow - pEdge->flow0, bChangeFlow );
            if (!IS_BNS_ERROR( ret_val ))
            {
                nChanges |= ( ret_val > 0 );
            }
            else
            {
                bError = ret_val;
            }
        }
        pEdge->pass = 0;
    }

    return bError ? bError : nChanges;
}


/****************************************************************************/
int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd )
{
    int ifcd, iedge;
    Vertex v1, v2;
    BNS_EDGE *pEdge;

    /* Find the next to the last changed */
    for (ifcd = 0; NO_VERTEX != ( iedge = fcd[ifcd].iedge ); ifcd++) /* djb-rwth: ignoring LLVM warning: variable used */
    {
        ;
    }

    /* Restore in reversed order to correctly handle vertex changed more than once */
    for (ifcd -= 1; 0 <= ifcd; ifcd--)
    {
        /* Restore edge flow & cap */
        iedge = fcd[ifcd].iedge;
        pEdge = pBNS->edge + iedge;
        pEdge->flow = fcd[ifcd].flow;
        pEdge->cap = fcd[ifcd].cap;
        pEdge->pass = 0;

        /* Restore st-flow, cap */
        if (NO_VERTEX != ( v1 = fcd[ifcd].v1 ))
        {
            pBNS->vert[v1].st_edge.flow = fcd[ifcd].flow_st1;
            pBNS->vert[v1].st_edge.cap = fcd[ifcd].cap_st1;
            pBNS->vert[v1].st_edge.pass = 0;
        }
        if (NO_VERTEX != ( v2 = fcd[ifcd].v2 ))
        {
            pBNS->vert[v2].st_edge.flow = fcd[ifcd].flow_st2;
            pBNS->vert[v2].st_edge.cap = fcd[ifcd].cap_st2;
            pBNS->vert[v2].st_edge.pass = 0;
        }
    }

    return 0;
}


/****************************************************************************/
int bSetFlowToCheckOneBond( BN_STRUCT *pBNS,
                            int iedge,
                            int flow,
                            BNS_FLOW_CHANGES *fcd )
{
    BNS_EDGE *pEdge = pBNS->edge + iedge;
    int       f12 = ( pEdge->flow & EDGE_FLOW_MASK ); /* the original flow */
    int       ifcd = 0;
    int       nDots = 0;
    int       i, iedge_i;

    fcd[ifcd].iedge = NO_VERTEX;

    if (f12 < flow)
    {
        /* Increase edge flow: Grab flow from the neighbors and delete it: set flow12=cap12 = 0 */
        /************************************************************************************/
        /* For example, simulate a new fixed double bond in place of a single bond and      */
        /* creates ONE or NONE (in case of a radical on adjacent atom) augmenting paths and */
        /* makes it impossible for the BNS to set same flow as it originally was            */
        /************************************************************************************/
        Vertex    v1 = pEdge->neighbor1;
        Vertex    v2 = v1 ^ pEdge->neighbor12;
        Vertex    v_i;        /* neighbor of v1 or v2 */
        BNS_EDGE *pEdge_i;
        int       delta1, delta2, f, st_edge_rescap;

        if (( pBNS->vert[v1].st_edge.cap  & EDGE_FLOW_ST_MASK ) < flow ||
            ( pBNS->vert[v2].st_edge.cap  & EDGE_FLOW_ST_MASK ) < flow)
        {
            return BNS_CANT_SET_BOND;
        }
        if (( pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK ) < f12 ||
            ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK ) < f12)
        {
            return BNS_CAP_FLOW_ERR;
        }

        fcd[ifcd].iedge = iedge;
        fcd[ifcd].flow = pEdge->flow;
        fcd[ifcd].cap = pEdge->cap;

        fcd[ifcd].v1 = v1;
        fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow;
        fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap;

        fcd[ifcd].v2 = v2;
        fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow;
        fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap;

        fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */
        pEdge->pass |= 64;

        delta1 = delta2 = flow - f12;

        if (f12 > 0)
        {
            /* Remove old edge flow from the flow and cap of the adjacent vertices' st-edges */
            pBNS->vert[v1].st_edge.cap = ( ( pBNS->vert[v1].st_edge.cap  & EDGE_FLOW_ST_MASK ) - f12 ) | ( pBNS->vert[v1].st_edge.cap  & ~EDGE_FLOW_ST_MASK );
            pBNS->vert[v2].st_edge.cap = ( ( pBNS->vert[v2].st_edge.cap  & EDGE_FLOW_ST_MASK ) - f12 ) | ( pBNS->vert[v2].st_edge.cap  & ~EDGE_FLOW_ST_MASK );
            pBNS->vert[v1].st_edge.flow = ( ( pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK ) - f12 ) | ( pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK );
            pBNS->vert[v2].st_edge.flow = ( ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK ) - f12 ) | ( pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK );
            /* Delete current edge flow and capacity */
            pEdge->flow = ( pEdge->flow & ~EDGE_FLOW_MASK );
        }
        pEdge->cap = ( pEdge->cap  & ~EDGE_FLOW_MASK );

        /* Grab the adjacent vertex1 radical (st_edge_rescap) if it exists */
        st_edge_rescap = ( pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK ) - ( pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK );
        while (st_edge_rescap && delta1)
        {
            st_edge_rescap--;  /* grab the radical */
            delta1--;
            pBNS->vert[v1].st_edge.cap = ( ( pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK );
            nDots--;
        }

        /* Grab the adjacent vertex2 radical (st_edge_rescap) if it exists */
        st_edge_rescap = ( pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK ) - ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK );
        while (st_edge_rescap && delta2)
        {
            st_edge_rescap--;  /* grab the radical */
            delta2--;
            pBNS->vert[v2].st_edge.cap = ( ( pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK );
            nDots--;
        }

        /* Grab flows from v1 neighbors */
        for (i = 0; delta1 && i < pBNS->vert[v1].num_adj_edges; i++)
        {
            iedge_i = pBNS->vert[v1].iedge[i];
            if (iedge_i == iedge)
            {
                continue;
            }
            pEdge_i = pBNS->edge + iedge_i;
            if (IS_FORBIDDEN( pEdge_i->forbidden, pBNS ))
            {
                continue;
            }
            f = ( pEdge_i->flow & EDGE_FLOW_MASK );
            if (f)
            {
                v_i = pEdge_i->neighbor12 ^ v1;

                fcd[ifcd].iedge = iedge_i;
                fcd[ifcd].flow = pEdge_i->flow;
                fcd[ifcd].cap = pEdge_i->cap;

                fcd[ifcd].v1 = v_i;
                fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow;
                fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap;

                fcd[ifcd].v2 = NO_VERTEX;
                fcd[ifcd].flow_st2 = 0;
                fcd[ifcd].cap_st2 = 0;

                fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */
                pEdge_i->pass |= 64;

                while (f && delta1)
                {
                    f--;
                    delta1--;
                    pEdge_i->flow = ( ( pEdge_i->flow & EDGE_FLOW_MASK ) - 1 ) | ( pEdge_i->flow & ~EDGE_FLOW_MASK );
                    pBNS->vert[v_i].st_edge.flow = ( ( pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK );
                    /* next 2 lines added 01-22-2002 */
                    pBNS->vert[v1].st_edge.cap = ( ( pBNS->vert[v1].st_edge.cap  & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v1].st_edge.cap  & ~EDGE_FLOW_ST_MASK );
                    pBNS->vert[v1].st_edge.flow = ( ( pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK );

                    nDots++;
                }
            }
        }

        /* Grab flows from v2 neighbors */
        for (i = 0; delta2 && i < pBNS->vert[v2].num_adj_edges; i++)
        {
            iedge_i = pBNS->vert[v2].iedge[i];
            if (iedge_i == iedge)
            {
                continue;
            }
            pEdge_i = pBNS->edge + iedge_i;
            if (IS_FORBIDDEN( pEdge_i->forbidden, pBNS ))
            {
                continue;
            }
            f = ( pEdge_i->flow & EDGE_FLOW_MASK );
            if (f)
            {
                v_i = pEdge_i->neighbor12 ^ v2;

                fcd[ifcd].iedge = iedge_i;
                fcd[ifcd].flow = pEdge_i->flow;
                fcd[ifcd].cap = pEdge_i->cap;

                fcd[ifcd].v1 = v_i;
                fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow;
                fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap;

                fcd[ifcd].v2 = NO_VERTEX;
                fcd[ifcd].flow_st2 = 0;
                fcd[ifcd].cap_st2 = 0;

                fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */
                pEdge_i->pass |= 64;

                while (f && delta2)
                {
                    f--;
                    delta2--;
                    pEdge_i->flow = ( ( pEdge_i->flow & EDGE_FLOW_MASK ) - 1 ) | ( pEdge_i->flow & ~EDGE_FLOW_MASK );
                    pBNS->vert[v_i].st_edge.flow = ( ( pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK );
                    /* next 2 lines added 01-22-2002 */
                    pBNS->vert[v2].st_edge.cap = ( ( pBNS->vert[v2].st_edge.cap  & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v2].st_edge.cap  & ~EDGE_FLOW_ST_MASK );
                    pBNS->vert[v2].st_edge.flow = ( ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK ) - 1 ) | ( pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK );

                    nDots++;
                }
            }
        }
        if (delta1 || delta2)
        {
            return BNS_CANT_SET_BOND;
        }
    }

    if (f12 >= flow)
    {
        /* Decrease edge flow: Redirect flow to the neighbors and delete it on the edge: set flow12=cap12 = 0 */
        /* f12==flow fixes flow through the edge so that BNS cannot change it */
        /**********************************************************************************************/
        /* For example, simulate a removal of a double bond and create ONE or NONE augmenting path    */
        /* Make it impossible for BNS to set same flow as it originally was                           */
        /**********************************************************************************************/
        Vertex    v1 = pEdge->neighbor1;
        Vertex    v2 = ( v1 ^ pEdge->neighbor12 );
        int       delta;
        /* if NOT (st-cap >= st-flow >= f12 >= flow) then error in the BN structure */
        if (( pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK ) < f12 ||
            ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK ) < f12 ||
             ( pBNS->vert[v1].st_edge.cap  & EDGE_FLOW_ST_MASK ) < flow ||
             ( pBNS->vert[v2].st_edge.cap  & EDGE_FLOW_ST_MASK ) < flow)
        {
            return BNS_CAP_FLOW_ERR;
        }
        fcd[ifcd].iedge = iedge;
        fcd[ifcd].flow = pEdge->flow;
        fcd[ifcd].cap = pEdge->cap;

        fcd[ifcd].v1 = v1;
        fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow;
        fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap;

        fcd[ifcd].v2 = v2;
        fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow;
        fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap;

        fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */
        pEdge->pass |= 64;

        delta = f12 - flow;

        /* Remove current edge flow from st-edges */
        /* -- seem to be a bug --
        pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK);
        pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK);
        */

        /* Replacement to the above 2 lines 01-16-2002 */
        /* Remove old edge flow from the flow of the adjacent vertices' st-edges */

        pBNS->vert[v1].st_edge.flow = ( ( pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK ) - f12 ) | ( pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK );
        pBNS->vert[v2].st_edge.flow = ( ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK ) - f12 ) | ( pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK );

        /* Sdded 01-16-2002: reduce st-cap if new flow > 0  */
        /* Remove new edge flow from the cap of the adjacent vertices' st-edges */
        pBNS->vert[v1].st_edge.cap = ( ( pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK ) - flow ) | ( pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK );
        pBNS->vert[v2].st_edge.cap = ( ( pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK ) - flow ) | ( pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK );

        /* delete current edge flow and capacity */
        pEdge->flow = ( pEdge->flow & ~EDGE_FLOW_MASK );
        pEdge->cap = ( pEdge->cap  & ~EDGE_FLOW_MASK );
        nDots = 2 * delta;
    }

    return nDots;
}


/****************************************************************************
bAddNewVertex( ... )

Connect new (fictitious, temporary) vertex to nVertDoubleBond by a new edge
Add radical (set st-cap=1) to the new vertex, set cap=1 to the new edge
Add radical (set st-cap=1) to nVertSingleBond
Find augmenting path connecting new vertex to nVertSingleBond
This corresponds to moving H-atom from nVertSingleBond to nVertDoubleBond
****************************************************************************/
int bAddNewVertex( BN_STRUCT *pBNS,
                   int nVertDoubleBond,
                   int nCap,
                   int nFlow,
                   int nMaxAdjEdges,
                   int *nDots )
{
    Vertex      vlast = pBNS->num_vertices - 1;
    Vertex      vnew = pBNS->num_vertices;
    Vertex      v2 = nVertDoubleBond;
    BNS_VERTEX *pVert2 = pBNS->vert + v2;   /* pointer to an old vertex */
    BNS_VERTEX *pNewVert = pBNS->vert + vnew; /* pointer to a new vertex */

    EdgeIndex   iedge = pBNS->num_edges;
    BNS_EDGE   *pEdge = pBNS->edge + iedge; /* pointer to a new edge */

    if (iedge >= pBNS->max_edges || vnew >= pBNS->max_vertices)
    {
        return BNS_VERT_EDGE_OVFL; /* edges or vertices overflow */
    }
    if (( pBNS->vert[vlast].iedge - pBNS->iedge ) + pBNS->vert[vlast].max_adj_edges + nMaxAdjEdges >= pBNS->max_iedges)
    {
        return BNS_VERT_EDGE_OVFL; /* iedges overflow */
    }
    if (pVert2->num_adj_edges >= pVert2->max_adj_edges || nMaxAdjEdges <= 0)
    {
        return BNS_VERT_EDGE_OVFL; /* neighbors overflow */
    }

    /* Fill out the new edge, set its cap and flow, connect */
    /* memset( pEdge, 0, sizeof(*pEdge) ); */
    pEdge->cap = pEdge->cap0 = nCap;
    pEdge->flow = pEdge->flow0 = nFlow;
    pEdge->pass = 0;
    pEdge->neighbor1 = v2;
    pEdge->neighbor12 = v2 ^ vnew;
    pEdge->forbidden = 0;

    /* Fill out the new vertex */
    /* memset( pNewVert, 0, sizeof(*pNewVert) ); */
    pNewVert->max_adj_edges = nMaxAdjEdges;
    pNewVert->num_adj_edges = 0;
    pNewVert->st_edge.cap0 = pNewVert->st_edge.cap = nCap;
    pNewVert->st_edge.flow0 = pNewVert->st_edge.flow = nFlow;
    pNewVert->st_edge.pass = 0; /* add initialization; added 2006-03-25 */
    pNewVert->iedge = pBNS->vert[vlast].iedge + pBNS->vert[vlast].max_adj_edges;
    pNewVert->type = BNS_VERT_TYPE_TEMP;
    *nDots += nCap - nFlow;

    pEdge->neigh_ord[v2 > vnew] = pVert2->num_adj_edges;
    pEdge->neigh_ord[v2 < vnew] = pNewVert->num_adj_edges;

    /* Connect new edge to v2 */
    pVert2->iedge[pVert2->num_adj_edges++] = iedge;
    /* Connect new edge to vnew */
    pNewVert->iedge[pNewVert->num_adj_edges++] = iedge;

    /* Fix v2 flow and cap */
    *nDots -= (int) pVert2->st_edge.cap - (int) pVert2->st_edge.flow;
    pVert2->st_edge.flow += nFlow;
    if (pVert2->st_edge.cap < pVert2->st_edge.flow)
    {
        pVert2->st_edge.cap = pVert2->st_edge.flow;
    }
    *nDots += (int) pVert2->st_edge.cap - (int) pVert2->st_edge.flow;

    pBNS->num_edges++;
    pBNS->num_vertices++;

    return vnew;
}


/****************************************************************************/
int AddNewEdge( BNS_VERTEX *p1,
                BNS_VERTEX *p2,
                BN_STRUCT *pBNS,
                int nEdgeCap,
                int nEdgeFlow )
{
    int ip1 = (int) ( p1 - pBNS->vert );
    int ip2 = (int) ( p2 - pBNS->vert );
    int ie = pBNS->num_edges;
    BNS_EDGE *e = pBNS->edge + ie;

    /* Debug: check bounds */
    if (ip1 >= pBNS->max_vertices || ip1 < 0 ||
         ip2 >= pBNS->max_vertices || ip2 < 0 ||
         ie >= pBNS->max_edges || ie < 0 ||
         ( p1->iedge - pBNS->iedge ) < 0 ||
         ( p1->iedge - pBNS->iedge ) + p1->max_adj_edges > pBNS->max_iedges ||
         ( p2->iedge - pBNS->iedge ) < 0 ||
         ( p2->iedge - pBNS->iedge ) + p2->max_adj_edges > pBNS->max_iedges ||
         p1->num_adj_edges >= p1->max_adj_edges ||
         p2->num_adj_edges >= p2->max_adj_edges)
    {
        return BNS_VERT_EDGE_OVFL;
    }

    /* Clear the edge */
    memset( e, 0, sizeof( *e ) ); /* djb-rwth: memset_s C11/Annex K variant? */

    /* Connect */
    e->neighbor1 = inchi_min( ip1, ip2 );
    e->neighbor12 = ip1 ^ ip2;
    p1->iedge[p1->num_adj_edges] = ie;
    p2->iedge[p2->num_adj_edges] = ie;
    e->neigh_ord[ip1 > ip2] = p1->num_adj_edges++;
    e->neigh_ord[ip1 < ip2] = p2->num_adj_edges++;
    e->cap = e->cap0 = nEdgeCap;
    e->flow = e->flow0 = nEdgeFlow;
    p1->st_edge.flow += nEdgeFlow;
    p2->st_edge.flow += nEdgeFlow;
    if (p1->st_edge.cap < p1->st_edge.flow)
    {
        p1->st_edge.cap = p1->st_edge.flow;
    }
    if (p2->st_edge.cap < p2->st_edge.flow)
    {
        p2->st_edge.cap = p2->st_edge.flow;
    }
    pBNS->num_edges++;

    return ie;
}


/****************************************************************************/
BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type )
{
    if (v1 < pBNS->num_atoms)
    {
        Vertex      v2;
        BNS_EDGE   *pEdge1;
        BNS_VERTEX *pVert1 = pBNS->vert + v1;
        int         i = pVert1->num_adj_edges - 1;

        while (0 <= i)
        {
            pEdge1 = pBNS->edge + pVert1->iedge[i];
            v2 = pEdge1->neighbor12 ^ v1;
            if (pBNS->vert[v2].type == type)
            {
                return IS_FORBIDDEN( pEdge1->forbidden, pBNS ) ? NO_VERTEX : pVert1->iedge[i];
            }
            i--;
        }
        return NO_VERTEX; /* not found t-group */
    }
    else
    {
        if (v1 < pBNS->num_vertices)
        {
            return NO_VERTEX;
        }
    }

    return BNS_VERT_EDGE_OVFL;
}


/****************************************************************************/
Vertex GetGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type )
{
    if (v1 < pBNS->num_atoms)
    {
        Vertex      v2;
        BNS_EDGE   *pEdge1;
        BNS_VERTEX *pVert1 = pBNS->vert + v1;
        int         i = pVert1->num_adj_edges - 1;

        AT_NUMB type2;

        if (type == BNS_VERT_TYPE_ENDPOINT)
        {
            type2 = BNS_VERT_TYPE_TGROUP;
        }
        else
        {
            if (type == BNS_VERT_TYPE_C_POINT)
            {
                type2 = BNS_VERT_TYPE_C_GROUP;
            }
            else
            {
                type2 = 0;
            }
        }

        if (( pVert1->type & type ) == type)
        {
            while (0 <= i)
            {
                pEdge1 = pBNS->edge + pVert1->iedge[i];
                v2 = pEdge1->neighbor12 ^ v1;
                if (pBNS->vert[v2].type == type2)
                {
                    if (IS_FORBIDDEN( pEdge1->forbidden, pBNS ))
                    {
                        return NO_VERTEX;
                    }
                    return v2;
                }
                i--;
            }
        }
        return BNS_BOND_ERR; /* not found t-group */
    }
    else
    {
        if (v1 < pBNS->num_vertices)
        {
            return NO_VERTEX;
        }
    }

    return BNS_VERT_EDGE_OVFL;
}


/****************************************************************************/
int bAddStCapToAVertex( BN_STRUCT *pBNS,
                        Vertex v1,
                        Vertex v2,
                        VertexFlow *nOldCapVertSingleBond,
                        int *nDots,
                        int bAdjacentDonors )
{
    BNS_VERTEX *pVert1 = pBNS->vert + v1;
    BNS_VERTEX *pVert;
    BNS_EDGE   *pEdge;
    Vertex      v;
    int         i, n;
    VertexFlow  nNewCap;

    /* Change v1: increment its st-cap */
    n = 0;
    nOldCapVertSingleBond[n++] = pVert1->st_edge.cap;
    /*if ( pVert1->st_edge.cap == pVert1->st_edge.flow ) {*/
    pVert1->st_edge.cap++;
    *nDots += 1;

    /*}*/
    /* increment caps of adjacent edges if
    (1) the neighbor has st-cap != 0 and
    (2) (edge cap==0) OR (nSumEdgeCap < pVert1->st_edge.cap && pVert->st_edge.flow > pVert1->st_edge.cap)
    */
    if (!( pVert1->type & BNS_VERT_TYPE_ANY_GROUP ))
    {
        /*
        AT_NUMB nSumEdgeCap = 0;
        for ( i = 0; i < pVert1->num_adj_edges; i ++ ) {
        pEdge = pBNS->edge + pVert1->iedge[i];
        nSumEdgeCap += pEdge->cap;
        }
        */
        /* do not increment caps of t-group or c-group edges */
        for (i = 0; i < pVert1->num_adj_edges; i++)
        {
            pEdge = pBNS->edge + pVert1->iedge[i];
            nOldCapVertSingleBond[n++] = pEdge->cap; /* save edge cap */
            v = pEdge->neighbor12 ^ v1;
            if (v == v2 && !bAdjacentDonors)
            {
                continue;
            }
            pVert = pBNS->vert + v;
            if (pVert->type & BNS_VERT_TYPE_ANY_GROUP)
                continue;
            nNewCap = inchi_min( pVert->st_edge.cap, pVert1->st_edge.cap );
            nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP );
            pEdge->cap = nNewCap; /* change edge cap */
                                  /*
                                  if ( pVert->st_edge.cap > 0 && !pEdge->cap ) {
                                  pEdge->cap ++;
                                  } else
                                  if ( pVert->st_edge.flow > pVert1->st_edge.cap &&
                                  pEdge->cap < MAX_BOND_EDGE_CAP &&
                                  nSumEdgeCap < pVert1->st_edge.cap ) {
                                  pEdge->cap ++;
                                  }
                                  */
        }
    }

    return n; /* number of elements in nOldCapVertSingleBond[*] */
}


#define BNS_CHK_ALTP_NO_ALTPATH  0
#define BNS_CHK_ALTP_SAME_TGROUP 1
#define BNS_CHK_ALTP_SAME_VERTEX 2
#define BNS_CHK_ALTP_SET_SUCCESS 4


/****************************************************************************/
int bSetBnsToCheckAltPath( BN_STRUCT *pBNS,
                           int nVertDoubleBond,
                           int nVertSingleBond,
                           AT_NUMB type,
                           int path_type,
                           ALT_PATH_CHANGES *apc,
                           BNS_FLOW_CHANGES *fcd,
                           int *nDots )
{

    if (!pBNS->vert[nVertDoubleBond].st_edge.flow &&

         !( path_type == ALT_PATH_MODE_REM2H_CHG ||
            path_type == ALT_PATH_MODE_ADD2H_CHG ||
            path_type == ALT_PATH_MODE_REM2H_TST ||
            path_type == ALT_PATH_MODE_ADD2H_TST )
         )
    {
        return BNS_CHK_ALTP_NO_ALTPATH;
    }
    else
    {
        Vertex      vNew;
        Vertex      v1 = nVertSingleBond;
        Vertex      v2 = nVertDoubleBond;

        BNS_VERTEX *pVert1 = pBNS->vert + v1;
        BNS_VERTEX *pVert2 = pBNS->vert + v2;
        int n, bAdjacentDonors = 0;
        int ifcd = 0;

        Vertex     t1 = NO_VERTEX;
        Vertex     t2 = NO_VERTEX;
        int        iapc;

        /*#if ( TEST_REMOVE_S_ATOMS == 1 )*/ /* && ALT_PATH_MODE_4_SALT == path_type */
        if (( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) &&
             ALT_PATH_MODE_4_SALT2 == path_type &&
             ( BNS_VERT_TYPE_ENDPOINT & type ))
        {
            /*
            ---------------------------------------------------------
            \   action |  DB action (v2)   |   SB action (v1)  |
            vertex \        | accept H @ vertex | donate H @ vertex |
            type     \      | nVertDoubleBond   | nVertSingleBond   |
            ----------------+-------------------+-------------------+
            -ZH (v1)    |  error            |   -ZH(.)          |
            (cap>0 on edge  |                   |   increment       |
            except v1-v2)  |                   |   st-cap on Z     |
            ----------------+-------------------+-------------------+
            =Z  (v2)    |  =Z-(.)           |   error           |
            (st-flow>0)   |  add fict vertex  |                   |
            |  with st-cap=1    |                   |
            ----------------+-------------------+-------------------+
            endpoint      |  T(.)             |   T-(.)           |
            of t-group    |  increment        |   add fict vertex |
            represented   |  st-cap on T      |   with st-cap=1   |
            by fictitious |                   |                   |
            vertex T      |                   |                   |
            ---------------------------------------------------------
            */

            int         bSet_v1;  /* indicator: v1 has been set */
            int         bSet_v2;  /* indicator: v2 has been set */
            int         i;

            Vertex      v1t = NO_VERTEX;
            Vertex      v2t = NO_VERTEX;
            Vertex      v1Act, v2Act;
            Vertex      v;

            memset( apc, 0, sizeof( *apc ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            fcd[ifcd].iedge = NO_VERTEX;
            *nDots = 0;

            if (v1 == v2)
            {
                return BNS_CHK_ALTP_SAME_VERTEX;
            }

            /* Check whether v1 has neighbors adjacent to multiple bonds */
            for (i = 0, n = 0; i < pVert1->num_adj_edges; i++)
            {
                v = ( pBNS->edge + pVert1->iedge[i] )->neighbor12 ^ v1; /* v is adjacent to v1 */
                if (v == v2)
                {
                    continue; /* ignore connection to v2 */
                }
                n += ( pBNS->vert[v].st_edge.cap > 0 );
            }
            if (!n)
            {
                return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */
            }

            v1Act = v1;
            v2Act = v2;

            /* find t-group that contains v1 */
            if (( pVert1->type & type ) == type)
            {
                v1t = GetGroupVertex( pBNS, v1, type );
                if (IS_BNS_ERROR( v1t ))
                {
                    return v1t;
                }
                if (v1t != NO_VERTEX)
                {
                    v1Act = v1t;
                }
            }
            /* Find t-group that contains v2 */
            if (( pVert2->type & type ) == type)
            {
                v2t = GetGroupVertex( pBNS, v2, type );
                if (IS_BNS_ERROR( v2t ))
                {
                    return v2t;
                }
                if (v2t != NO_VERTEX)
                {
                    v2Act = v2t;
                }
            }
            if (v1t != NO_VERTEX && v1t == v2t)
            {
                return BNS_CHK_ALTP_SAME_TGROUP;
            }

            bSet_v1 = bSet_v2 = 0;
            /* create new edges adjacent to v1t or v2 */
            iapc = 0;
            if (v1t != NO_VERTEX)
            {
                /* Create new edge and vertex, connect to v1t */
                vNew = bAddNewVertex( pBNS, v1t, 1, 0, 1, nDots );
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                bSet_v1 = 1;
                iapc++;
            }
            if (v2t == NO_VERTEX)
            {
                /* Create new edge and vertex, connect to v2 */
                vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots );
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                bSet_v2 = 1;
                iapc++;
            }

            /* Add st-cap to v1 and/or v2t */
            iapc = 0;
            if (!bSet_v1)
            {
                /* Add st-cap to v1 */
                if (v1t != NO_VERTEX) /* djb-rwth: addressing coverity ID #499551 -- condition properly written */
                {
                    return BNS_BOND_ERR;
                }
                n = bAddStCapToAVertex( pBNS, v1, v2Act, apc->nOldCapsVert[iapc], nDots, 0 );
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = v1;
                iapc++;
            }
            if (!bSet_v2)
            {
                /* Add st-cap to v2t */
                if (v2t == NO_VERTEX)
                {
                    return BNS_BOND_ERR;
                }
                n = bAddStCapToAVertex( pBNS, v2t, v1Act, apc->nOldCapsVert[iapc], nDots, 0 );
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = v2t;
                iapc++;
            }
            if (*nDots < 0 || *nDots % 2)
            {
                return BNS_SET_ALTP_ERR;
            }
            return BNS_CHK_ALTP_SET_SUCCESS;
        }

        /* ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */

        /*#endif*/

        /*  ( TEST_REMOVE_S_ATOMS == 1 && ALT_PATH_MODE_4_SALT == path_type ) */

        if (path_type == ALT_PATH_MODE_REM2H_CHG ||
             path_type == ALT_PATH_MODE_ADD2H_CHG ||
             path_type == ALT_PATH_MODE_REM2H_TST ||
             path_type == ALT_PATH_MODE_ADD2H_TST)
        {
            /* added 2004-03-18 */

            int bDonors = ( path_type == ALT_PATH_MODE_REM2H_CHG ) || ( path_type == ALT_PATH_MODE_REM2H_TST );

            int         bSet_v1;  /* indicator: v1 has been set */
            int         bSet_v2;  /* indicator: v2 has been set */
            int         i, cap = 1;
            Vertex      v1t = NO_VERTEX;
            Vertex      v2t = NO_VERTEX;
            Vertex      v1Act, v2Act;
            Vertex      v;

            memset( apc, 0, sizeof( *apc ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            fcd[ifcd].iedge = NO_VERTEX;
            *nDots = 0;
            /*
            if ( v1 == v2 ) {
            return BNS_CHK_ALTP_SAME_VERTEX;
            }
            */

            /* Check whether v1 and v2 have proper neighbors  */
            for (i = 0, n = bAdjacentDonors = 0; i < pVert1->num_adj_edges; i++)
            {
                v = ( pBNS->edge + pVert1->iedge[i] )->neighbor12 ^ v1; /* v is adjacent to v1 */
                                                                        /* do not ignore connection to v2
                                                                        if ( v == v2 )
                                                                        continue;
                                                                        */
                n += bDonors ? ( pBNS->vert[v].st_edge.cap > 0 )
                    : ( ( pBNS->edge + pVert1->iedge[i] )->flow > 0 );
                bAdjacentDonors += bDonors ? ( v == v2 ) && ( ( pBNS->edge + pVert1->iedge[i] )->flow < MAX_BOND_EDGE_CAP ) : 0;
                /* two donors connected by a single or double bond */
            }

            if (!n && !bAdjacentDonors)
            {
                return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */
            }
            for (i = 0, n = bAdjacentDonors = 0; i < pVert2->num_adj_edges; i++)
            {
                v = ( pBNS->edge + pVert2->iedge[i] )->neighbor12 ^ v2; /* v is adjacent to v2 */
                                                                        /* do not ignore connection to v1
                                                                        if ( v == v1 )
                                                                        continue;
                                                                        */
                n += bDonors ? ( pBNS->vert[v].st_edge.cap > 0 ) : ( ( pBNS->edge + pVert2->iedge[i] )->flow > 0 );
                bAdjacentDonors += bDonors ? ( v == v1 ) && ( ( pBNS->edge + pVert2->iedge[i] )->flow < MAX_BOND_EDGE_CAP ) : 0;
                /* two donors connected by a single or double bond */
            }

            if (!n && !bAdjacentDonors)
            {
                return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */
            }

            v1Act = v1;
            v2Act = v2;

            /* Find t-group that contains v1 */
            if (( pVert1->type & type ) == type)
            {
                v1t = GetGroupVertex( pBNS, v1, type );
                if (BNS_BOND_ERR == v1t)
                {
                    v1t = NO_VERTEX;
                }
                else
                {
                    if (IS_BNS_ERROR( v1t ))
                    {
                        return v1t;
                    }
                    else
                    {
                        if (v1t != NO_VERTEX)
                        {
                            v1Act = v1t;
                        }
                    }
                }
            }

            /* Find t-group that contains v2 */
            if (( pVert2->type & type ) == type)
            {
                v2t = GetGroupVertex( pBNS, v2, type );
                if (BNS_BOND_ERR == v2t)
                {
                    v2t = NO_VERTEX;
                }
                else
                {
                    if (IS_BNS_ERROR( v2t ))
                    {
                        return v2t;
                    }
                    else
                    {
                        if (v2t != NO_VERTEX)
                        {
                            v2Act = v2t;
                        }
                    }
                }
            }

            if (v1t != NO_VERTEX && v1t == v2t)
            {
                cap = 2; /* same t-group */
            }

            /*  bAddNewVertex: (bDonors != 0) == (vit != NO_VERTEX), i=1,2 */
            bSet_v1 = bSet_v2 = 0;
            /* create new edges adjacent to v1t or v2 */
            iapc = 0;
            if (( bDonors != 0 ) == ( v1t != NO_VERTEX ))
            {
                /* Create new edge and vertex, connect to v1Act */
                vNew = bAddNewVertex( pBNS, v1Act, cap, 0, 1, nDots );
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                bSet_v1 = 1;
                iapc++;
            }
            if (( bDonors != 0 ) == ( v2t != NO_VERTEX ) && cap == 1)
            {
                /* Create new edge and vertex, connect to v2Act; do not do it if cap==2 */
                vNew = bAddNewVertex( pBNS, v2Act, cap, 0, 1, nDots );
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                bSet_v2 = 1;
                iapc++;
            }
            else
            {
                if (( bDonors != 0 ) == ( v2t != NO_VERTEX ))
                {
                    bSet_v2 = 1;
                }
            }

            /* Add st-cap to v1 and/or v2t */
            iapc = 0;
            /* If cap=2 then just increment st_cap 2 times */
            if (!bSet_v1)
            {
                /* Add st-cap to v1 */
                if (( bDonors != 0 ) == ( v1t != NO_VERTEX ))
                {
                    return BNS_BOND_ERR;
                }
                n = bAddStCapToAVertex( pBNS, v1Act, v2Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors );
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = v1Act;
                iapc++;
            }
            if (!bSet_v2)
            {
                /* Add st-cap to v2t */
                if (( bDonors != 0 ) == ( v2t != NO_VERTEX ))
                {
                    return BNS_BOND_ERR;
                }
                n = bAddStCapToAVertex( pBNS, v2Act, v1Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors );
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = v2Act;
                iapc++;
            }
            if (*nDots < 0 || *nDots % 2)
            {
                return BNS_SET_ALTP_ERR;
            }
            return BNS_CHK_ALTP_SET_SUCCESS;
        }

        if (path_type == ALT_PATH_MODE_REM_PROTON)
        {
            /* added 2004-03-05 */
            if (v1 >= 0 && v2 >= 0 &&
                ( pVert1->type & BNS_VERT_TYPE_ANY_GROUP ) &&
                 ( pVert2->type & BNS_VERT_TYPE_ANY_GROUP ))
            {
                /* Create new edge and vertex, connect to v2 */
                if (( pBNS->vert[v1].type & BNS_VERT_TYPE_C_GROUP ) &&
                    ( pBNS->vert[v1].st_edge.flow == 2 * pBNS->vert[v1].num_adj_edges ))
                {
                    /* so far in a charge group max edge flow = 1 2004-03-08 */
                    return BNS_CHK_ALTP_NO_ALTPATH;
                }
                memset( apc, 0, sizeof( *apc ) ); /* djb-rwth: memset_s C11/Annex K variant? */
                fcd[ifcd].iedge = NO_VERTEX;
                *nDots = 0;
                iapc = 0;

                vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots );
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                /*iapc ++;*/
                /* add st-cap (dot) to v1 */
                n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[iapc], nDots, 0 );
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = v1;
                iapc++;
                return BNS_CHK_ALTP_SET_SUCCESS;
            }
        }

#if ( NEUTRALIZE_ENDPOINTS == 1 ) /* { */

        *nDots = 0;
        memset( apc, 0, sizeof( *apc ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        fcd[ifcd].iedge = NO_VERTEX;

        if (type & BNS_VERT_TYPE_ENDPOINT)
        {
            BNS_IEDGE  iedge;
            AT_NUMB    type2;
            int ret2;
            /* prohibit charge movement */
            type2 = BNS_VERT_TYPE_C_GROUP;
            iedge = GetEdgeToGroupVertex( pBNS, v1, type2 );
            if (iedge != NO_VERTEX)
            {
                /*  Set flow=1 on an edge to a c-group vertex to make sure
                there is no positive charge when moving tautomeric H-atoms */
                ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd + ifcd );
                if (IS_BNS_ERROR( ret2 ))
                {
                    return ret2;
                }
                *nDots += ret2;
                while (fcd[ifcd].iedge != NO_VERTEX)
                {
                    ifcd++;
                }
            }

            iedge = GetEdgeToGroupVertex( pBNS, v2, type2 );

            if (iedge != NO_VERTEX)
            {
                /*  Set flow=1 on an edge to a c-group vertex to make sure
                there is no positive charge when moving tautomeric H-atoms */

                ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd + ifcd );

                if (IS_BNS_ERROR( ret2 ))
                {
                    return ret2;
                }
                *nDots += ret2;
                while (fcd[ifcd].iedge != NO_VERTEX)
                {
                    ifcd++;
                }
            }

            /* Set hydrogen counts */
            type2 = BNS_VERT_TYPE_TGROUP;
            iedge = GetEdgeToGroupVertex( pBNS, v1, type2 );
            if (iedge != NO_VERTEX)
            {
                /*  Set flow=1 on an edge to a t-group vertex to make sure there is
                a moveable hydrogen atom or (-) on v1 when moving tautomeric H-atoms */
#if ( FIX_H_CHECKING_TAUT == 1 )
                ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd + ifcd );
                if (IS_BNS_ERROR( ret2 ))
                {
                    return ret2;
                }
                *nDots += ret2;
                while (fcd[ifcd].iedge != NO_VERTEX)
                {
                    ifcd++;
                }
#else
                t1 = pBNS->edge[iedge].neighbor12 ^ v1;
#endif
            }

            iedge = GetEdgeToGroupVertex( pBNS, v2, type2 );
            if (iedge != NO_VERTEX)
            {
                /*  Set flow=0 on an edge to a t-group vertex to make sure there is
                no moveable hydrogen atom or (-) on v2 when moving tautomeric H-atoms  */
#if ( FIX_H_CHECKING_TAUT == 1 )
                ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 0, fcd + ifcd );
                if (IS_BNS_ERROR( ret2 ))
                {
                    return ret2;
                }
                *nDots += ret2;
                while (fcd[ifcd].iedge != NO_VERTEX)
                {
                    ifcd++;
                }
#else
                t2 = pBNS->edge[iedge].neighbor12 ^ v2;
#endif
            }

#if ( FIX_H_CHECKING_TAUT == 1 )
#else
            if (t1 == t2 && t1 != NO_VERTEX)
            {
                return BNS_CHK_ALTP_SAME_TGROUP;
            }
#endif

            iapc = 0;
            /* Create new edge and vertex with cap=1 at v2 and/or t1 */
            if (t1 != NO_VERTEX)
            {
                /* Create new edge and vertex, connect to t1 */
                vNew = bAddNewVertex( pBNS, t1, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); /* djb-rwth: addressing coverity ID #499581 -- condition works as expected for t1 == -2 */
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                iapc++;
            }
            if (t2 == NO_VERTEX)
            {
                /* Create new edge and vertex, connect to v2 */
                vNew = bAddNewVertex( pBNS, v2, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots );
                if (IS_BNS_ERROR( vNew ))
                {
                    return vNew;
                }
                apc->vNewVertex[iapc] = vNew;
                apc->bSetNew[iapc] = 1;
                iapc++;
            }

            /* Add st-cap to v1 and/or v2t */
            iapc = 0;
            if (t1 == NO_VERTEX)
            {
                /* Add st-cap to v1 */
                n = bAddStCapToAVertex(pBNS, v1, (Vertex)(t2 == NO_VERTEX ? v2 : t2), apc->nOldCapsVert[iapc], nDots, 0); /* djb-rwth: addressing coverity ID #499501 -- condition works as expected for t2 == -2 */
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = v1;
                iapc++;
            }
            if (t2 != NO_VERTEX)
            {
                /* Add st-cap to t2 */
                n = bAddStCapToAVertex( pBNS, t2, (Vertex) ( t1 == NO_VERTEX ? v1 : t1 ), apc->nOldCapsVert[iapc], nDots, 0 );
                apc->bSetOldCapsVert[iapc] = n;
                apc->vOldVert[iapc] = t2;
                iapc++;
            }
        }
        else
        {
            /* Create new edge and vertex, connect to v2 */
            vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots );
            if (IS_BNS_ERROR( vNew ))
            {
                return vNew;
            }
            apc->vNewVertex[0] = vNew;
            apc->bSetNew[0] = 1;

            /* add st-cap to v1 */
            n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots, 0 );
            apc->bSetOldCapsVert[0] = n;
            apc->vOldVert[0] = v1;
        }
#else  /* } NEUTRALIZE_ENDPOINTS == 0 {*/

        *nDots = 0;
        memset( apc, 0, sizeof( *apc ) );
        fcd[ifcd].iedge = NO_VERTEX;

        /* Create new edge and vertex, connect to v2 */
        vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots, 0 );
        if (IS_BNS_ERROR( vNew ))
        {
            return vNew;
        }
        apc->vNewVertex[0] = vNew;
        apc->bSetNew[0] = 1;

        /* Add st-cap to v1 */
        n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots );
        apc->bSetOldCapsVert[0] = n;
        apc->vOldVert[0] = v1;
#endif /* } NEUTRALIZE_ENDPOINTS */

        if (*nDots < 0 || *nDots % 2)
        {
            return BNS_SET_ALTP_ERR;
        }
        return BNS_CHK_ALTP_SET_SUCCESS;
    }

    /*return BNS_CHK_ALTP_NO_ALTPATH;*/
}


/****************************************************************************/
int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS,
                                  ALT_PATH_CHANGES *apc,
                                  int bChangeFlow )
    /* int nVertDoubleBond, int nVertSingleBond, int nNewVertex, AT_NUMB *nOldCapVertSingleBond */
{
    BNS_EDGE   *pEdge;
    Vertex      vNew;
    Vertex      vOld;
    BNS_VERTEX *pOldVert;
    BNS_VERTEX *pNewVert;
    int i, j, n; /* djb-rwth: removing redundant variables */

    /* djb-rwth: removing redundant code */

    if (bChangeFlow & BNS_EF_UPD_H_CHARGE)
    {
        /* Remove new temp. vertices and edges connectong them to the structure */
        for (i = sizeof( apc->bSetNew ) / sizeof( apc->bSetNew[0] ) - 1; 0 <= i; i--)
        {
            if (apc->bSetNew[i])
            {
                vNew = apc->vNewVertex[i];
                pNewVert = pBNS->vert + vNew;
                for (j = 0; j < pNewVert->num_adj_edges; j++)
                {
                    pEdge = pBNS->edge + pNewVert->iedge[j];
                    vOld = pEdge->neighbor12 ^ vNew;
                    pOldVert = pBNS->vert + vOld;
                    pOldVert->st_edge.flow -= pEdge->flow;
                    pOldVert->st_edge.cap -= pEdge->flow;
                    /* disconnect new edge from pOldVert */
                    pOldVert->iedge[--pOldVert->num_adj_edges] = 0;
                    /* clear the new edge */
                    memset( pEdge, 0, sizeof( *pEdge ) ); /* djb-rwth: memset_s C11/Annex K variant? */
                    /* and decrement the total number of edges */
                    pBNS->num_edges--;
                }
                /* Clear the new vertex */
                memset( pNewVert, 0, sizeof( *pNewVert ) ); /* djb-rwth: memset_s C11/Annex K variant? */
                /*  and decrement the total number of vertices
                (new vertice ids are contiguous)            */
                pBNS->num_vertices--;
                /* djb-rwth: removing redundant code */
            }
        }

        /* Restore changed caps of old vertices */
        for (i = sizeof( apc->bSetOldCapsVert ) / sizeof( apc->bSetOldCapsVert[0] ) - 1;
              0 <= i;
              i--)
        {
            if ((n = apc->bSetOldCapsVert[i])) /* djb-rwth: addressing LLVM warning */
            {
                pOldVert = pBNS->vert + apc->vOldVert[i];
                if (pOldVert->st_edge.flow <= apc->nOldCapsVert[i][0])
                {
                    pOldVert->st_edge.cap = apc->nOldCapsVert[i][0];
                    n--;
                    /* djb-rwth: removing redundant code */
                    for (j = 0; j < n && j < pOldVert->num_adj_edges; j++)
                    {
                        pEdge = pBNS->edge + pOldVert->iedge[j];
                        pEdge->cap = apc->nOldCapsVert[i][j + 1];
                    }
                }
            }
        }
    }
    else
    {
        /* Restore changed caps of old vertices */
        for (i = sizeof( apc->bSetOldCapsVert ) / sizeof( apc->bSetOldCapsVert[0] ) - 1; 0 <= i; i--)
        {
            if ((n = apc->bSetOldCapsVert[i])) /* djb-rwth: addressing LLVM warning */
            {
                pOldVert = pBNS->vert + apc->vOldVert[i];
                pOldVert->st_edge.cap = apc->nOldCapsVert[i][0];
                n--;
                /* djb-rwth: removing redundant code */
                for (j = 0; j < n && j < pOldVert->num_adj_edges; j++)
                {
                    pEdge = pBNS->edge + pOldVert->iedge[j];
                    pEdge->cap = apc->nOldCapsVert[i][j + 1];
                }
            }
        }

        /* Remove new temp. vertices and edges connectong them to the structure */
        for (i = sizeof( apc->bSetNew ) / sizeof( apc->bSetNew[0] ) - 1; 0 <= i; i--)
        {
            if (apc->bSetNew[i])
            {
                vNew = apc->vNewVertex[i];
                pNewVert = pBNS->vert + vNew;
                for (j = 0; j < pNewVert->num_adj_edges; j++)
                {
                    pEdge = pBNS->edge + pNewVert->iedge[j];
                    vOld = pEdge->neighbor12 ^ vNew;
                    pOldVert = pBNS->vert + vOld;
                    /* disconnect new edge from pOldVert */
                    pOldVert->iedge[--pOldVert->num_adj_edges] = 0;
                    /* clear the new edge */
                    memset( pEdge, 0, sizeof( *pEdge ) ); /* djb-rwth: memset_s C11/Annex K variant? */
                    /* and decrement the total number of edges */
                    pBNS->num_edges--;
                }
                /* Clear the new vertex */
                memset( pNewVert, 0, sizeof( *pNewVert ) ); /* djb-rwth: memset_s C11/Annex K variant? */
                /* and decrement the total number of vertices (new vertice ids are contiguous */
                pBNS->num_vertices--;
                /* djb-rwth: removing redundant code */
            }
        }
    }

    return 0;
}


/****************************************************************************/
int bExistsAnyAltPath( CANON_GLOBALS *pCG,
                       BN_STRUCT *pBNS,
                       BN_DATA *pBD,
                       inp_ATOM *at,
                       int num_atoms,
                       int nVert2,
                       int nVert1,
                       int path_type )
{
    int nRet1, nRet2;

    nRet1 = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, nVert2, nVert1, path_type );

    if (nRet1 > 0)
    {
        return nRet1;
    }

    nRet2 = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, nVert1, nVert2, path_type );

    if (nRet2 > 0)
    {
        return nRet2;
    }
    if (IS_BNS_ERROR( nRet1 ))
    {
        return nRet1;
    }
    if (IS_BNS_ERROR( nRet2 ))
    {
        return nRet2;
    }

    return 0;
}


#define ALT_PATH_TAUTOM 1
#define ALT_PATH_CHARGE 2
#define ALT_PATH_4_SALT 3


/****************************************************************************/
int bIsBnsEndpoint( BN_STRUCT *pBNS, int v )
{
    int i, vt;
    BNS_VERTEX    *pVert;  /* vertices */
    BNS_EDGE      *pEdge;  /* edges */

    if (0 <= v && v < pBNS->num_atoms && ( pVert = pBNS->vert + v ) && ( pVert->type & BNS_VERT_TYPE_ENDPOINT ))
    {
        for (i = pVert->num_adj_edges - 1; 0 <= i; i--)
        {
            pEdge = pBNS->edge + pVert->iedge[i];
            vt = pEdge->neighbor12 ^ v;
            if (pBNS->vert[vt].type & BNS_VERT_TYPE_TGROUP)
            {
                return !IS_FORBIDDEN( pEdge->forbidden, pBNS );
            }
        }
    }

    return 0;
}


#if ( BNS_RAD_SEARCH == 1 )


/****************************************************************************/
int bRadChangesAtomType( BN_STRUCT *pBNS,
                         BN_DATA *pBD,
                         Vertex v,
                         Vertex v_1,
                         Vertex v_2 )
{

    EdgeIndex iuv;
    Vertex v_O, v_ChgOrH;

    /* The previous atom along the path: should be a terminal atom */
    if (v_1 == NO_VERTEX)
    {
        v_1 = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv );
    }
    v_O = v_1 / 2 - 1;

    if (v_O < 0 || v_O >= pBNS->num_atoms)
    {
        return 0;
    }

    /* Make sure v_O is a terminal atom: its second neighbor is not an atom */
    if (pBNS->vert[pBNS->edge[pBNS->vert[v_O].iedge[1]].neighbor12 ^ v_O].type & BNS_VERT_TYPE_ATOM)
    {
        return 0;
    }

    /* The next to previous vertex vertex along the path: should be a Charge or Taut group vertex */
    if (v_2 == NO_VERTEX)
    {
        v_2 = GetPrevVertex( pBNS, v_1, pBD->SwitchEdge, &iuv );
    }

    v_ChgOrH = v_2 / 2 - 1;
    if (v_ChgOrH < pBNS->num_atoms)
    {
        return 0;
    }

    /* Make sure v_ChgOrH is a charge or taut_group */
    if (pBNS->vert[v_ChgOrH].type & ( BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP ))
    {
        return 1;
    }

    return 0;
}


/****************************************************************************/
int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u )
{
    EdgeIndex iuv;
    int       i, num_found;
    Vertex    v, w;
    Vertex    u_last, v2;
    switch (pBD->bRadSrchMode)
    {
        case  RAD_SRCH_NORM:
            /* Go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */
            /* we need only vertices where a radical may be moved, therefore exclude u%2=1 (odd) vertices */
            /* atom number = u/2-1; u = 0 or 1 is 's' or 't' vertices, respectively, they are not atoms  */
            num_found = 0;
            while (u > Vertex_t && ( u % 2 || u / 2 > pBNS->num_atoms ))
            {
                u = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv );
            }
            w = u / 2 - 1; /* Check whether u is a radical endpoint */
            if (Vertex_t < u && w < pBNS->num_atoms &&
                 pBNS->vert[w].st_edge.cap == ( pBNS->vert[w].st_edge.flow & EDGE_FLOW_ST_MASK ))
            {
                /* u is an atom; it is not a radical atom */
                /* now search for the starting radical atom by following the path back from u */
                v = u_last = u;
                while (v > Vertex_t)
                {
                    u = v;
                    v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); /* Radical endpoint */
                }
                /* Check whether u is a radical atom */
                if (!( u % 2 ) && Vertex_t < u &&
                    ( u = u / 2 - 1 ) < pBNS->num_atoms &&
                     pBNS->vert[u].st_edge.cap > ( pBNS->vert[u].st_edge.flow & EDGE_FLOW_ST_MASK ))
                {
                    /* at pBNS->vert[u] we have found the radical that originated the path */
                    /* pBD->RadEndpoints[2k] is the radical, pBD->RadEndpoints[2k+1] is the farthest atom */
                    /* to which the radical may be moved (farthest reachable atom) */

                    /* add *all* atoms that may receive radical from u_rad */
                    /* exception: at2 in: ==(+/-/H)---at1==at2(possible rad endpoint) if pBNS->type_TACN */
                    for (v = u_last; v > Vertex_t; v = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ))
                    {
                        if (!( v % 2 ) && ( v2 = v / 2 - 1 ) < pBNS->num_atoms &&
                             pBNS->vert[v2].st_edge.cap == ( pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK ))
                        {
                            /* Check exception */
                            if (pBNS->type_TACN &&
                                 bRadChangesAtomType( pBNS, pBD, v, NO_VERTEX, NO_VERTEX ))
                            {
                                continue;
                            }
                            /* Add */
                            for (i = 0; i < pBD->nNumRadEndpoints; i += 2)
                            {
                                /* Check whether this pair, (u,w), has already been saved */
                                if (u == pBD->RadEndpoints[i] &&
                                     v2 == pBD->RadEndpoints[i + 1])
                                {
                                    break;
                                }
                            }
                            if (i >= pBD->nNumRadEndpoints)
                            {
                                /* Add new (u,w) pair */
                                if (pBD->nNumRadEndpoints + 2 <= pBD->max_num_vertices)
                                {
                                    /* add */
                                    pBD->RadEndpoints[pBD->nNumRadEndpoints++] = u; /* radical */
                                    pBD->RadEndpoints[pBD->nNumRadEndpoints++] = v2; /* endpoint */
                                    num_found++;
                                    /*return 1;*/ /* registered */
                                }
                                else
                                {
                                    return BNS_VERT_EDGE_OVFL;
                                }
                            }
                        }
                    }
                    if (num_found)
                    {
                        return 1;
                    }
                }
            }
            break;

        case RAD_SRCH_FROM_FICT:
            /* Find the nearest atom accessible from a fictitious vertex */
            /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */

            v = u;
            w = NO_VERTEX; /* the nearest atom -- radical-endpoint */
            u = NO_VERTEX; /* fictitious vertex carrying a radical */

            while (v > Vertex_t)
            {
                u = v;
                if (!( v % 2 ) && v / 2 <= pBNS->num_atoms &&
                     pBNS->vert[v / 2 - 1].st_edge.cap - pBNS->vert[v / 2 - 1].st_edge.flow < 2)
                {
                    w = v; /* vertex w is atom that may be singlet or doublet but not triplet */
                }
                v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv );
            }
            v = u / 2 - 1; /* vertex u may be the radical from which the path originated; w is the nearest atom */
            if (w == NO_VERTEX || u == NO_VERTEX || w % 2 || u == w || v < pBNS->num_atoms ||
                 pBNS->vert[v].st_edge.cap == pBNS->vert[v].st_edge.flow ||
                 ( w = w / 2 - 1 ) >= pBNS->num_atoms)
            {
                break; /* reject */
            }
            u = v;
            /* At pBNS->vert[u] we have found the radical that originated the path, w is the nearest atom */
            for (i = 0; i < pBD->nNumRadEndpoints; i += 2)
            {
                if (u == pBD->RadEndpoints[i] &&
                     w == pBD->RadEndpoints[i + 1])
                {
                    break; /* this pair has already been stored */
                }
            }
            if (i >= pBD->nNumRadEndpoints)
            {
                /* A new pair has been found */
                if (pBD->nNumRadEndpoints + 2 <= pBD->max_num_vertices)
                {
                    /* Add */
                    pBD->RadEndpoints[pBD->nNumRadEndpoints++] = u; /* radical */
                    pBD->RadEndpoints[pBD->nNumRadEndpoints++] = w; /* endpoint */
                    return 1; /* registered */
                }
                else
                {
                    return BNS_VERT_EDGE_OVFL;
                }
            }
            break;
    }

    return 0; /* rejected */
}


/****************************************************************************/
int cmp_rad_endpoints( const void *a1, const void *a2 )
{
    /* Vertex radical_vertex, radical_endpoint */
    const Vertex *p1 = (const Vertex *) a1;
    const Vertex *p2 = (const Vertex *) a2;

    if (p1[0] < p2[0])
    {
        return -1;
    }
    if (p1[0] > p2[0])
    {
        return 1;
    }
    if (p1[1] < p2[1])
    {
        return -1;
    }
    if (p1[1] > p2[1])
    {
        return 1;
    }

    return 0;
}


/****************************************************************************/
int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at )
{
    BNS_EDGE   *e;
    EdgeIndex   ie;
    BNS_VERTEX *p1, *p2;
    Vertex      v1, v2;
    int         i, delta, rad;

    for (i = pBD->nNumRadEdges - 1; 0 <= i; i--)
    {
        ie = pBD->RadEdges[i];
        if (ie < 0 || ie >= pBNS->num_edges)
        {
            goto error_exit;
        }
        e = pBNS->edge + ie;
        v1 = e->neighbor1;
        v2 = e->neighbor12 ^ v1;   /* v2 > v1 <=> v2 was added later */
        if (ie + 1 != pBNS->num_edges ||
             v1 < 0 || v1 >= pBNS->num_vertices ||
             v2 < 0 || v2 >= pBNS->num_vertices)
        {
            goto error_exit;
        }
        p1 = pBNS->vert + v1;
        p2 = pBNS->vert + v2;

        if (p2->iedge[p2->num_adj_edges - 1] != ie ||
             p1->iedge[p1->num_adj_edges - 1] != ie)
        {
            goto error_exit;
        }

        p2->num_adj_edges--;
        p1->num_adj_edges--;
        p2->iedge[p2->num_adj_edges] = 0;
        p1->iedge[p1->num_adj_edges] = 0;
        p2->st_edge.flow -= e->flow;
        p1->st_edge.flow -= e->flow;

        if (!p2->num_adj_edges && v2 >= pBNS->num_atoms)
        {
            if (v2 + 1 != pBNS->num_vertices)
            {
                goto error_exit;
            }
            memset( p2, 0, sizeof( *p2 ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            pBNS->num_vertices--;
        }

        if (!p1->num_adj_edges && v1 >= pBNS->num_atoms)
        {
            if (v1 + 1 != pBNS->num_vertices)
            {
                goto error_exit;
            }
            memset( p1, 0, sizeof( *p1 ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            pBNS->num_vertices--;
        }

        if (at && v1 < pBNS->num_atoms)
        {
            delta = p1->st_edge.cap - p1->st_edge.flow;
            rad = at[v1].radical;
            switch (delta)
            {
                case 0:
                    if (rad == RADICAL_DOUBLET)
                    {
                        rad = 0;
                    }
                    break;
                case 1:
                    if (rad != RADICAL_DOUBLET)
                    {
                        rad = RADICAL_DOUBLET;
                    }
            }
            at[v1].radical = rad;
        }
        memset( e, 0, sizeof( *e ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        pBNS->num_edges--;
    }
    pBD->nNumRadEdges = 0;
    pBD->nNumRadicals = 0;
    pBD->bRadSrchMode = RAD_SRCH_NORM;
    return 0;

error_exit:

    return BNS_PROGRAM_ERR;
}


/****************************************************************************/
int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at )
{
    BNS_EDGE   *e;
    EdgeIndex   ie;
    BNS_VERTEX *p1, *p2;
    Vertex      v1, v2;
    int         i, delta, rad;
    int         p1_num_adj_edges, p2_num_adj_edges;

    for (i = pBD->nNumRadEdges - 1; 0 <= i; i--)
    {
        ie = pBD->RadEdges[i];
        if (ie < 0 || ie >= pBNS->num_edges)
        {
            goto error_exit;
        }
        e = pBNS->edge + ie;
        v1 = e->neighbor1;         /* atom */
        v2 = e->neighbor12 ^ v1;   /* v2 > v1 <=> v2 was added later */
        if (v1 < 0 || v1 >= pBNS->num_atoms ||
             v2 < pBNS->num_atoms || v2 >= pBNS->num_vertices)
        {
            goto error_exit;
        }
        p1 = pBNS->vert + v1;
        p2 = pBNS->vert + v2;

        p1_num_adj_edges = e->neigh_ord[0];
        p2_num_adj_edges = e->neigh_ord[1];

        if (p2->iedge[p2_num_adj_edges] != ie ||
             p1->iedge[p1_num_adj_edges] != ie)
        {
            goto error_exit;
        }

        if (at && v1 < pBNS->num_atoms)
        {
            delta = p1->st_edge.cap - p1->st_edge.flow + e->flow;
            rad = at[v1].radical;
            switch (delta)
            {
                case 0:
                    if (rad == RADICAL_DOUBLET)
                        rad = 0;
                    break;
                case 1:
                    if (rad != RADICAL_DOUBLET)
                        rad = RADICAL_DOUBLET;
            }
            at[v1].radical = rad;
        }
    }
    return 0;

error_exit:

    return BNS_PROGRAM_ERR;
}


/****************************************************************************/
int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
{
    int ret, i, j, k, delta; /* djb-rwth: removing redundant variables */
    BNS_VERTEX *pRad, *pEndp;
    Vertex     wRad, vRad, vEndp, nNumRadicals;
    int        nDots = 0 /* added initialization, 2006-03 */, nNumEdges;

    if (pBNS->tot_st_cap <= pBNS->tot_st_flow)
    {
        return 0;
    }

    pBD->nNumRadEndpoints = 0;
    pBD->nNumRadEdges = 0;
    pBD->bRadSrchMode = bRadSrchMode;
    pBNS->alt_path = pBNS->altp[0];
    pBNS->bChangeFlow = 0;
    ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH );
    ReInitBnData( pBD );
    ReInitBnStructAltPaths( pBNS );
    if (!ret && pBD->nNumRadEndpoints >= 2)
    {
        /* Sort by radical locations */
        qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints / 2, 2 * sizeof( pBD->RadEndpoints[0] ), cmp_rad_endpoints );
        /* djb-rwth: removing redundant code */
        nNumRadicals = 0;

        /* Create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 */
        /* connecting the new vertices radical vertices */
        for (i = 0; i < pBD->nNumRadEndpoints; i = j)
        {
            wRad = pBD->RadEndpoints[i];
            pRad = pBNS->vert + wRad;
            delta = pRad->st_edge.cap - ( pRad->st_edge.flow & EDGE_FLOW_ST_MASK );
            if (delta <= 0)
            {
                delta = 1;
            }
            nNumEdges = 0;
            for (j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j]; j += 2)
            {
                nNumEdges++;
            }
            /* Add new aux vertex to the radical atom/vertex */
            vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges + 1, &nDots );
            if (IS_BNS_ERROR( vRad ))
            {
                ret = vRad;
                goto error_exit;
            }
            pRad = pBNS->vert + vRad;
            pBD->RadEdges[pBD->nNumRadEdges++] = pRad->iedge[pRad->num_adj_edges - 1];
            /* Replace references to vertex wRad with vRad */
            for (k = i, nNumEdges = 0; k < j; k += 2) /* djb-rwth: ignoring LLVM warning: variable used */
            {
                pBD->RadEndpoints[k] = vRad;
            }
            nNumRadicals++;
        }
        /* All vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */
        /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */
        for (i = 0; i < pBD->nNumRadEndpoints; i = j)
        {
            vRad = pBD->RadEndpoints[i];
            pRad = pBNS->vert + vRad;
            for (j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j]; j += 2)
            {
                /* Connect vew vertex pRad to radical endpoints */
                vEndp = pBD->RadEndpoints[j + 1];
                pEndp = pBNS->vert + vEndp;
                ret = AddNewEdge( pRad, pEndp, pBNS, 1, 0 );
                if (IS_BNS_ERROR( ret ))
                {
                    goto error_exit;
                }
                pBD->RadEdges[pBD->nNumRadEdges++] = ret;
            }
        }
        pBD->nNumRadicals = nNumRadicals;
        return nNumRadicals; /* done */
    }

    return 0; /* nothing to do */

error_exit:
    RemoveRadEndpoints( pBNS, pBD, NULL );

    return ret;
}


#define MAX_NUM_RAD  256


/****************************************************************************/
int SetRadEndpoints2( CANON_GLOBALS *pCG,
                      BN_STRUCT *pBNS,
                      BN_DATA *pBD,
                      BRS_MODE bRadSrchMode )
{
    int ret = 0, i, j, k, n, delta = 1; /* djb-rwth: removing redundant variables */
    BNS_VERTEX *pRad, *pEndp;
    Vertex     wRad, vRad, vEndp, nNumRadicals;
    Vertex     vRadList[MAX_NUM_RAD], vRadEqul[MAX_NUM_RAD];
    int        nNumRad = 0;
    int        edge_flow;
    int        nDots = 0 /* added initialization, 2006-03 */, nNumEdges;
    NodeSet    VertSet;

    if (pBNS->tot_st_cap <= pBNS->tot_st_flow)
    {
        return 0;
    }

    /* Find all radicals: their vertices have st_cap-st_flow=delta */
    /* save radical atom numbers in vRadList[] and remove radical by making st_cap=st_flow */
    for (i = 0; i < pBNS->num_atoms; i++)
    {
        if (pBNS->vert[i].st_edge.cap - delta == ( pBNS->vert[i].st_edge.flow & EDGE_FLOW_ST_MASK ))
        {
            if (nNumRad < MAX_NUM_RAD)
            {
                pBNS->vert[i].st_edge.cap -= delta;
                pBNS->tot_st_cap -= delta;
                vRadList[nNumRad] = i;       /* radical position; i > j <=> vRadList[i] > vRadList[j]  */
                vRadEqul[nNumRad] = nNumRad; /* the smallest radical atom that has reachable
                                             * atoms in common with this radical atom
                                             * always keep vRadEqul[nNumRad] <= nNumRad */
                nNumRad++;
            }
        }
    }

    if (pBNS->tot_st_cap - pBNS->tot_st_flow > nNumRad)
    {
        return BNS_CAP_FLOW_ERR; /* extra st_cap on non-atoms or program error */
    }

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

    /* Find reachable atoms by enabling each radical separately */
    for (j = 0; j < nNumRad; j++)
    {
        i = vRadList[j];
        pBD->nNumRadEndpoints = 0;
        pBD->nNumRadEdges = 0;
        pBD->bRadSrchMode = bRadSrchMode;
        pBNS->alt_path = pBNS->altp[0];
        pBNS->bChangeFlow = 0;
        pBNS->vert[i].st_edge.cap += delta; /* enable single radical */
        pBNS->tot_st_cap += delta;
        ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); /* find reachable atoms */
        ReInitBnData( pBD );
        ReInitBnStructAltPaths( pBNS );
        pBD->bRadSrchMode = RAD_SRCH_NORM;
        pBNS->vert[i].st_edge.cap -= delta; /* disable single radical */
        pBNS->tot_st_cap -= delta;
        if (IS_BNS_ERROR( ret ))
        {
            goto error_exit;
        }
        else
        {
            if (ret)
            {
                ret = BNS_RADICAL_ERR; /* found augmenting path: should not happen since only one radical was enabled */
                goto error_exit;
            }
        }
        if (!ret && pBD->nNumRadEndpoints >= 2)
        {
            /* Sort by: primary_key=radical locations, secondary_key=radical endoint */

            qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints / 2, 2 * sizeof( pBD->RadEndpoints[0] ), cmp_rad_endpoints );

            if (pBD->RadEndpoints[0] != i || pBD->RadEndpoints[pBD->nNumRadEndpoints - 2] != i)
            {
                ret = BNS_RADICAL_ERR; /* more than one radical vertex */
                goto error_exit;
            }
            if (nNumRad > 1)
            {
                /* If more than one radical then save reachable atoms in bitmaps to allow */
                /* faster finding whether same atoms are reachable by two or more radicals */
                /* Later merge such sets */
                if (NULL == VertSet.bitword)
                {
                    SetBitCreate( pCG );
                    if (!NodeSetCreate( pCG, &VertSet, pBNS->num_atoms, nNumRad ))
                    {
                        ret = BNS_OUT_OF_RAM; /* out of RAM */
                        goto error_exit;
                    }
                }

                NodeSetFromRadEndpoints( pCG, &VertSet, j, pBD->RadEndpoints, pBD->nNumRadEndpoints );

                /* Do not allow any radical center be treated as a reachable atom: */

                RemoveFromNodeSet( pCG, &VertSet, j, vRadList, nNumRad );
            }
        }
    }

    /* Restore radical st_cap so that st_cap-st_flow=delta */
    for (j = 0; j < nNumRad; j++)
    {
        i = vRadList[j];
        pBNS->vert[i].st_edge.cap += delta;
        pBNS->tot_st_cap += delta;
    }

    /* Merge lists that have common radical endpoints */
    /* defect: if vertex sets i and j do not intersect they will be compared 2 times */
    /* total up to nNumRad*(nNumRad-1)/2 calls to DoNodeSetsIntersect() */
    if (nNumRad > 1)
    {
        for (i = 0; i < nNumRad; i++)
        {
            if (vRadEqul[i] != i)
            {
                continue;
            }
            do
            {
                n = 0;
                for (j = i + 1; j < nNumRad; j++)
                {
                    if (vRadEqul[j] != j)
                    {
                        continue;
                    }
                    if (DoNodeSetsIntersect( &VertSet, i, j ))
                    {
                        AddNodeSet2ToNodeSet1( &VertSet, i, j );
                        vRadEqul[j] = i; /* Set j was copied to set i; i < j */
                        n++;
                    }
                }
            } while (n);
        }
        /* Fill out pBD->RadEndpoints[] */
        for (i = 0, n = 0; i < nNumRad; i++)
        {
            if (i == vRadEqul[i])
            {
                if (!IsNodeSetEmpty( &VertSet, i ))
                {
                    /* Store equivalent radicals */
                    for (j = i + 1; j < nNumRad; j++)
                    {
                        if (i == vRadEqul[j])
                        {
                            pBD->RadEndpoints[n++] = vRadList[i];
                            pBD->RadEndpoints[n++] = -vRadList[j] - 2; /* equivalent radical, alvays not zero */
                        }
                    }
                    /* Store endpoints */
                    n = AddNodesToRadEndpoints( pCG, &VertSet, i, pBD->RadEndpoints, vRadList[i], n, pBD->max_len_Pu_Pv );
                    if (n < 0)
                    {
                        ret = BNS_RADICAL_ERR; /* pBD->RadEndpoints overflow */
                        goto error_exit;
                    }
                }
                else
                {
                    pBD->RadEndpoints[n++] = vRadList[i];
                    pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */
                }
            }
        }
        pBD->nNumRadEndpoints = n;
        NodeSetFree( pCG, &VertSet );
    }
    else
    {
        if (nNumRad == 1 && !pBD->nNumRadEndpoints)
        {
            /* 2006-07-30: a single radical; no possible endpoint found */
            for (i = 0, n = 0; i < nNumRad; i++)
            {
                pBD->RadEndpoints[n++] = vRadList[i];
                pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */
            }
            pBD->nNumRadEndpoints = n;
        }
    }

    if (!ret && pBD->nNumRadEndpoints >= 2)
    {
        /* Already sorted by radical locations */
        /* djb-rwth: removing redundant code */
        nNumRadicals = 0;
        /**************************************************************************
        * Create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1
        * connecting the new vertices radical vertices
        *
        *
        * Original structure:    atom A is a radical center    A==B--C*--D==E
        *   A*--B==C--D==E       atoms C and E are reachable:  A==B--C===D--E*
        *
        * Resultant temporary structure:
        *   A---B==C--D==E
        *  ||     /     /
        *  ||    /    /          The additional new vertex (*) and its
        *  ||   /   /            3 edges replace the radical with alternating
        *  ||  /  /              circuits that allow same bond changes
        *  || / /                as moving the radical to atoms C or E.
        *  ||//                  "Double bonds" here have edge cap=1, flow=1
        *  (*)                   "Single bonds" have edge cap=1, flow=0
        *
        *   The "equivalent radical centers" (which have at least one reachable atom
        *   in common) are connected to (*) with "double bonds" (edge cap=1, flow=1).
        *   Reachable non-radical atoms are connected by edges with cap=1, flow=0
        *   After running BNS to find alt.path a "double bond" from (*) may move
        *   to another atom thus muving the radical.
        *
        *   Number of additional (*) vertices = number of sets of
        *   "equivalent radical centers".
        *   Each such a set may include one or more radical centers.
        *
        *   The radicals will be re-created in RemoveRadEndpoints()
        ***************************************************************************/
        for (i = 0; i < pBD->nNumRadEndpoints; i = j)
        {
            wRad = pBD->RadEndpoints[i];
            pRad = pBNS->vert + wRad;
            delta = pRad->st_edge.cap - ( pRad->st_edge.flow & EDGE_FLOW_ST_MASK );
            if (delta <= 0)
            {
                delta = 1;
            }
            nNumEdges = 0;
            for (j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j]; j += 2)
            {
                nNumEdges += ( pBD->RadEndpoints[j + 1] != -1 ); /* immobile radicals have one edge only */
            }
            /* Add new aux vertex to the radical atom/vertex making st_cap-st_flow=0 */
            /* in case of immobile radical there will be no additional eddges since nNumEdges=0 */
            vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges + 1, &nDots );
            if (IS_BNS_ERROR( vRad ))
            {
                ret = vRad;
                goto error_exit;
            }
            pRad = pBNS->vert + vRad;
            pBD->RadEdges[pBD->nNumRadEdges++] = pRad->iedge[pRad->num_adj_edges - 1];
            /* replace references to vertex wRad with vRad */
            for (k = i, nNumEdges = 0; k < j; k += 2) /* djb-rwth: ignoring LLVM warning: variable used */
            {
                pBD->RadEndpoints[k] = vRad;
            }
            nNumRadicals++;
        }
        /* All vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */
        /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */
        for (i = 0; i < pBD->nNumRadEndpoints; i = j)
        {
            vRad = pBD->RadEndpoints[i];
            pRad = pBNS->vert + vRad;
            for (j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j]; j += 2)
            {
                /* connect vew vertex pRad to radical endpoints */
                vEndp = pBD->RadEndpoints[j + 1];
                if (vEndp == -1)
                    continue;
                if (vEndp < 0)
                {
                    edge_flow = 1;
                    vEndp = -vEndp - 2; /* equivalent radical centers */
                }
                else
                {
                    edge_flow = 0;
                }
                pEndp = pBNS->vert + vEndp;
                ret = AddNewEdge( pRad, pEndp, pBNS, 1, edge_flow );
                if (IS_BNS_ERROR( ret ))
                {
                    goto error_exit;
                }
                pBD->RadEdges[pBD->nNumRadEdges++] = ret;
            }
        }
        pBD->nNumRadicals = nNumRadicals;
        return nNumRadicals; /* done */
    }
    return 0; /* nothing to do */

error_exit:
    RemoveRadEndpoints( pBNS, pBD, NULL );
    NodeSetFree( pCG, &VertSet );

    return ret;
}


#else
/****************************************************************************/
int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
{
    return 0;
}
int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at )
{
    return 0;
}
int SetRadEndpoints2( CANON_GLOBALS *pCG, BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
{
    return 0;
}
#endif


/****************************************************************************
bExistsAltPath( ... )

Return value ret bits if not IS_BNS_ERROR(ret):

ret & 1         => Success
ret & 2         => Bonds changed to Alt
(ret & ~3) >> 2 => nDelta: number of removed dots
****************************************************************************/
int bExistsAltPath( CANON_GLOBALS *pCG,
                    BN_STRUCT *pBNS,
                    BN_DATA *pBD,
                    BN_AATG *pAATG,
                    inp_ATOM *at,
                    int num_atoms,
                    int nVertDoubleBond,
                    int nVertSingleBond,
                    int path_type )
{
    ALT_PATH_CHANGES apc;
    int ret, ret_val, bError, bSuccess, bChangeFlow = 0, nDots, nDelta, bDoMarkChangedBonds = 1;
    int bAdjustRadicals = 0;
    AT_NUMB          type;
    BNS_FLOW_CHANGES fcd[4 * BNS_MAX_NUM_FLOW_CHANGES + 1];
    ENDPOINT_INFO    eif;
#if ( KETO_ENOL_TAUT == 1 )
    ENDPOINT_INFO    eif2;
#endif

    /* Initialize */
    switch (path_type)
    {
        case ALT_PATH_MODE_TAUTOM:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                ( !nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ))
            {
                return 0;
            }
            if (!at[nVertDoubleBond].endpoint &&
                ( !nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ))
            {
                return 0;
            }
            break;

#if ( TAUT_PT_22_00 == 1 )	
        case ALT_PATH_MODE_TAUTOM_PT_22_00:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                (!nGetEndpointInfo_PT_22_00(at, nVertSingleBond, &eif) || !eif.cDonor))
                return 0;
            if (!at[nVertDoubleBond].endpoint &&
                (!nGetEndpointInfo_PT_22_00(at, nVertDoubleBond, &eif) || !eif.cAcceptor))
                return 0;
            break;
#endif	

#if ( TAUT_PT_16_00 == 1 )	
        case ALT_PATH_MODE_TAUTOM_PT_16_00:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                (!nGetEndpointInfo_PT_16_00(at, nVertSingleBond, &eif) || !eif.cDonor))
                return 0;
            if (!at[nVertDoubleBond].endpoint &&
                (!nGetEndpointInfo_PT_16_00(at, nVertDoubleBond, &eif) || !eif.cAcceptor))
                return 0;
            break;
#endif

#if ( TAUT_PT_06_00 == 1 )	
        case ALT_PATH_MODE_TAUTOM_PT_06_00:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                (!nGetEndpointInfo_PT_06_00(at, nVertSingleBond, &eif) || !eif.cDonor))
                return 0;
            if (!at[nVertDoubleBond].endpoint &&
                (!nGetEndpointInfo_PT_06_00(at, nVertDoubleBond, &eif) || !eif.cAcceptor))
                return 0;
            break;
#endif

#if ( TAUT_PT_39_00 == 1 )	
        case ALT_PATH_MODE_TAUTOM_PT_39_00:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                (!nGetEndpointInfo_PT_39_00(at, nVertSingleBond, &eif) || !eif.cDonor))
                return 0;
            if (!at[nVertDoubleBond].endpoint &&
                (!nGetEndpointInfo_PT_39_00(at, nVertDoubleBond, &eif) || !eif.cAcceptor))
                return 0;
            break;
#endif		

#if ( TAUT_PT_13_00 == 1 )	
        case ALT_PATH_MODE_TAUTOM_PT_13_00:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                (!nGetEndpointInfo_PT_13_00(at, nVertSingleBond, &eif) || !eif.cDonor))
                return 0;
            if (!at[nVertDoubleBond].endpoint &&
                (!nGetEndpointInfo_PT_13_00(at, nVertDoubleBond, &eif) || !eif.cAcceptor))
                return 0;
            break;
#endif

#if ( TAUT_PT_18_00 == 1 )	
        case ALT_PATH_MODE_TAUTOM_PT_18_00:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            if (!at[nVertSingleBond].endpoint &&
                (!nGetEndpointInfo_PT_18_00(at, nVertSingleBond, &eif) || !eif.cDonor))
                return 0;
            if (!at[nVertDoubleBond].endpoint &&
                (!nGetEndpointInfo_PT_18_00(at, nVertDoubleBond, &eif) || !eif.cAcceptor))
                return 0;
            break;
#endif	


#if ( KETO_ENOL_TAUT == 1 )
        case ALT_PATH_MODE_TAUTOM_KET:
            /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = BNS_EF_CHNG_RSTR;

            if (!at[nVertSingleBond].endpoint &&
                ( !nGetEndpointInfo_KET( at, nVertSingleBond, &eif ) || !eif.cDonor ))
            {
                return 0;
            }
            if (!at[nVertDoubleBond].endpoint &&
                ( !nGetEndpointInfo_KET( at, nVertDoubleBond, &eif2 ) || !eif2.cAcceptor ))
            {
                return 0;
            }
            /*
            if ( eif.cKetoEnolCode + eif2.cKetoEnolCode != 3 )
            return 0;
            */
            break;

#endif
        case ALT_PATH_MODE_CHARGE:
            /* Find alt path allowing to move (+). Purpose: establish "charge groups",
            mark alt. bonds due to (+) charge movement */
            type = BNS_VERT_TYPE_C_POINT;
            bChangeFlow = ( BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS );
            break;

        case ALT_PATH_MODE_4_SALT:
        case ALT_PATH_MODE_4_SALT2:
            /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms".
            Purpose: mark alt bonds due to this "long range" tautomerism. */
            type = BNS_VERT_TYPE_ENDPOINT;
            bChangeFlow = ( BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS );
            if (!bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ &&
                ( !nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ))
            {
                return 0;
            }
            if (!bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ &&
                ( !nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ))
            {
                return 0;
            }
            memset( &apc, 0, sizeof( apc ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            break;

        case ALT_PATH_MODE_REM2H_CHG:
            bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */
        case ALT_PATH_MODE_REM2H_TST:
            bChangeFlow |= BNS_EF_CHNG_RSTR;
            type = BNS_VERT_TYPE_ENDPOINT;
            /* Allow non-tautomeric donors or any tautomeric atom */
            if (!bIsBnsEndpoint( pBNS, nVertSingleBond ) /* not linked to a t-group or the edge forbidden */ &&
                ( !nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor )) /* not a donor */
            {
                return 0;
            }
            if (!bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* not connected to a t-group */ &&
                ( !nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cDonor ))
            {
                return 0;
            }
            memset( &apc, 0, sizeof( apc ) ); /* djb-rwth: memset_s C11/Annex K variant? */
            break;

        case ALT_PATH_MODE_ADD2H_CHG:
            bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */
        case ALT_PATH_MODE_ADD2H_TST:
            bChangeFlow |= BNS_EF_CHNG_RSTR;
            type = BNS_VERT_TYPE_ENDPOINT;
            /* Allow non-tautomeric acceptors or any tautomeric atom */
            if (!bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ &&
                ( !nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cAcceptor ))
            {
                return 0;
            }
            if (!bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertSingleBond].endpoint*/ &&
                ( !nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ))
            {
                return 0;
            }
            break;

        case ALT_PATH_MODE_REM_PROTON:
            /* alt path is between the t-group (nVertDoubleBond) and
            the (+)-charge group (nVertSingleBond) */
            type = 0;
            /*bDoMarkChangedBonds = 0;*/
            bChangeFlow = ( BNS_EF_SAVE_ALL | BNS_EF_UPD_H_CHARGE ) | BNS_EF_ALTR_NS; /* added BNS_EF_ALTR_NS: set non-stereo altern non-ring bonds 2004-07-02*/
            break;
        default:
            type = 0;
            bChangeFlow = BNS_EF_CHNG_RSTR;
            break;
    }

    bError = 0;
    bSuccess = 0;
    nDelta = 0;

    ret = SetRadEndpoints2( pCG, pBNS, pBD, RAD_SRCH_NORM );
    if (IS_BNS_ERROR( ret ))
    {
        return ret;
    }

    /* Set BNS to check alt path */
    ret = bSetBnsToCheckAltPath( pBNS, nVertDoubleBond, nVertSingleBond, type, path_type, &apc, fcd, &nDots );
    switch (ret)
    {
        case  BNS_CHK_ALTP_NO_ALTPATH:
            ret = RemoveRadEndpoints( pBNS, pBD, NULL );
            return ret;
        case BNS_CHK_ALTP_SAME_TGROUP:
            bSuccess = 1;
            goto reinit_BNS;
        case BNS_CHK_ALTP_SAME_VERTEX:
            ret = RemoveRadEndpoints( pBNS, pBD, NULL );
            return ret ? ret : 1; /* very strange ... set a breakpoint here */
        case BNS_CHK_ALTP_SET_SUCCESS:
            break;  /* actually check the existence of the altpath */
        case BNS_CANT_SET_BOND:
            goto reinit_BNS;
        default:
            ret_val = RemoveRadEndpoints( pBNS, pBD, NULL ); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
            if (IS_BNS_ERROR( ret ))
            {
                return ret;
            }
            return BNS_PROGRAM_ERR;
    }

    bAdjustRadicals = ( ( bChangeFlow & BNS_EF_UPD_RAD_ORI ) && !( bChangeFlow & BNS_EF_RSTR_FLOW ) );

    /*****************************************************************
    * nDots = 2 for ALT_PATH_CHARGE (checking moveable positive charges)
    * Now nDots for ALT_PATH_TAUTOM or ALT_PATH_4_SALT can be greater
    * because some of the bonds are effectively removed and dots
    * (vertex st-caps) may be added
    * -- to make sure there is no (+) charge on a tautomeric endpoint
    * -- to fix positions of moveable tautomeric attachements
    *    (H and (-)-charges) at the ends of an alt path
    */

    /* Run BNS */

    ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow );

    if (IS_BNS_ERROR( ret ))
    {
        bError = ret;
    }
    else if (ret > 0)
    {
        if (2 * ret >= nDots)
        {
            nDelta = 2 * ret - nDots;  /* non-zero means augmentation created another alt. path -- between radicals */
            if (pAATG && pAATG->nMarkedAtom)
            {
                if (pAATG->nAtTypeTotals && ( bChangeFlow & BNS_EF_UPD_H_CHARGE ))
                {
                    memset( pAATG->nMarkedAtom, 0, num_atoms * sizeof( pAATG->nMarkedAtom[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
                    /* Mark atoms that have charge or H changed, check their input types (that is, before changes),
                    and subtract their input charge/H from nAtTypeTotals */
                    SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom, NULL, 1 );
                    /* ZChange charges and/or H, update t_group_info, do not check types or change nAtTypeTotals */
                    /* Atom types will be checked and nAtTypeTotals will be changed in
                    AddChangedAtHChargeBNS() later */
                    SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, NULL, NULL, pAATG->t_group_info, 0 );
                }
                else
                {
                    if (!pAATG->nAtTypeTotals)
                    {
                        bDoMarkChangedBonds = MarkAtomsAtTautGroups( pBNS, num_atoms, pAATG, nVertSingleBond, nVertDoubleBond );
                        if (bDoMarkChangedBonds < 0)
                        {
                            bError = bDoMarkChangedBonds;
                            bDoMarkChangedBonds = 0;
                        }
                    }
                }
            }
            if (bDoMarkChangedBonds)
            {
                /* Mark bonds that were changed to configure bond testing */
                ret_val = bSetBondsAfterCheckOneBond( pBNS, fcd, -1, at, num_atoms, bChangeFlow );
                if (IS_BNS_ERROR( ret_val ))
                {
                    bError = ret_val;
                }
                /*ret = SetBondsRestoreBnStructFlow( pBNS, at, num_atoms, bChangeFlow );*/
                /* mark all other changed bonds */
                ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow );
                if (IS_BNS_ERROR( ret ))
                {
                    bError = ret;
                }
                else
                {
                    if (!( ret & 1 ) && !( ret_val & 1 ))
                    {
                        bSuccess = 1;
                    }
                    else
                    {
                        if ((( ( ret & 1 ) || ( ret_val & 1 ) ) &&
                            ( bChangeFlow & BNS_EF_ALTR_BONDS )) || ( bChangeFlow & BNS_EF_UPD_H_CHARGE )) /* djb-rwth: addressing LLVM warning */
                        {
                            /* Some bonds have been changed to alternating */
                            bSuccess = 3;
                        }
                        else
                        {
                            bError = BNS_BOND_ERR;
                        }
                    }
                }
                if (!bError && pAATG && pAATG->nMarkedAtom && ( bChangeFlow & BNS_EF_UPD_H_CHARGE ))
                {
                    /* Update radicals to avoid errors in atom type check in AddChangedAtHChargeBNS() */
                    if (bAdjustRadicals)
                    {
                        ret_val = RestoreRadicalsOnly( pBNS, pBD, at );
                        if (IS_BNS_ERROR( ret_val ))
                        {
                            bError = ret_val;
                        }
                    }
                    /* Check atom types of marked atoms and add charge/H changes to nAtTypeTotals */
                    /* Changing atoms were marked in the 1st call to SubtractOrChangeAtHChargeBNS(..., 1) above */
                    AddChangedAtHChargeBNS( at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom );
                    if (bChangeFlow & BNS_EF_CHNG_FLOW)
                    {
                        /* Eliminate ambiguities in already changed flow:
                        replace (+)--N==(-) with (+)==N--(-) (both represent neutral N) */
                        EliminatePlusMinusChargeAmbiguity( pBNS, num_atoms );
                    }
                }
            }
        }

        ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR );

        if (IS_BNS_ERROR( ret ))
        {
            bError = ret;
        }
    }

reinit_BNS:

    /* --- Reinitialize to repeat the calculations --- */
    bRestoreBnsAfterCheckAltPath( pBNS, &apc, bChangeFlow & BNS_EF_UPD_H_CHARGE );
    bRestoreFlowAfterCheckOneBond( pBNS, fcd );
    ret_val = RemoveRadEndpoints( pBNS, pBD, bAdjustRadicals ? at : NULL );
    ReInitBnStructAltPaths( pBNS );

    return bError ? bError : ret_val ? ret_val : ( bSuccess + 4 * nDelta );
}


/****************************************************************************/
BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at,
                                    int num_atoms,
                                    int nMaxAddAtoms,
                                    int nMaxAddEdges,
                                    int max_altp,
                                    int *pNum_changed_bonds )
{
    BN_STRUCT   *pBNS = NULL;
    BNS_VERTEX  *vert;

    int    neigh, num_changed_bonds = 0;
    U_CHAR bond_type, bond_mark;

    int i, j, k, n_edges, num_bonds, num_edges, f1, f2, edge_cap, edge_flow, st_flow; /* djb-rwth: removing redundant variables */
    int tot_st_cap, tot_st_flow;
    int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_altp;
#if ( BNS_RAD_SEARCH == 1 )
    int num_rad = 0;

    nMaxAddEdges += 1;
#endif
#if ( FIX_NUM_TG == 1 )
    max_tg = inchi_max( num_atoms / 2, 5 );
#else
    max_tg = num_atoms;
#endif
    num_changed_bonds = 0;

    for (i = 0, num_bonds = 0; i < num_atoms; i++)
    {
        /*(@nnuk : Nauman Ullah Khan) */
        LOG_NO_ARGS("\n################# (L8916:ichi_bns.c) ###################\n");
        LOG_MULT_ARGS("Number of changed bonds (Start): %d\n", num_changed_bonds);
        LOG_NO_ARGS("\n########################################################\n");

        num_bonds += at[i].valence;
#if ( BNS_RAD_SEARCH == 1 )
        num_rad += ( at[i].radical == RADICAL_DOUBLET );
#endif
    }
    /* Each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */
    /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */
    /* max_altp cannot be larger than BN_MAX_ALTP = 16 */
    num_edges = ( num_bonds /= 2 );
    /* +1 for a super-tautomeric group */
    max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1;
    /* +max_tg for edges between t-groups and super-tautomeric group */
    max_edges = num_edges + ( nMaxAddEdges + NUM_KINDS_OF_GROUPS )*max_vertices + max_tg;
#if ( BNS_RAD_SEARCH == 1 )
    if (num_rad)
    {
        max_vertices *= 2;
        max_edges *= 2;
    }
#endif
    max_iedges = 2 * max_edges;
    len_alt_path = max_vertices + iALTP_HDR_LEN + 1; /* may overflow if an edge is traversed in 2 directions */

    if (!( pBNS = (BN_STRUCT   *) inchi_calloc( 1, sizeof( BN_STRUCT ) ) ) ||
         !( pBNS->edge = (BNS_EDGE    *) inchi_calloc( max_edges, sizeof( BNS_EDGE ) ) ) ||
         !( pBNS->vert = (BNS_VERTEX  *) inchi_calloc( max_vertices, sizeof( BNS_VERTEX ) ) ) ||
         !( pBNS->iedge = (BNS_IEDGE   *) inchi_calloc( max_iedges, sizeof( BNS_IEDGE ) ) ))
    {
        return DeAllocateBnStruct( pBNS );
    }
    /* Alt path init */
    for (num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp++)
    {
        if (!( pBNS->altp[num_altp] = (BNS_ALT_PATH*) inchi_calloc( len_alt_path, sizeof( BNS_ALT_PATH ) ) ))
        {
            return DeAllocateBnStruct( pBNS );
        }
        ALTP_ALLOCATED_LEN( pBNS->altp[num_altp] ) = len_alt_path;
        pBNS->len_alt_path = len_alt_path;  /* ??? duplication ??? */
                                            /* re-init */
        ALTP_DELTA( pBNS->altp[num_altp] ) = 0;
        ALTP_START_ATOM( pBNS->altp[num_altp] ) = NO_VERTEX;
        ALTP_END_ATOM( pBNS->altp[num_altp] ) = NO_VERTEX;
        ALTP_PATH_LEN( pBNS->altp[num_altp] ) = 0;
    }
    pBNS->alt_path = NULL;
    pBNS->num_altp = 0;
    pBNS->max_altp = num_altp;

    /* Fill vertices (no connectivity) */
    pBNS->vert[0].iedge = pBNS->iedge;
    for (i = 0; i < num_atoms; i++)
    {
        k = pBNS->vert[i].max_adj_edges = at[i].valence + ( nMaxAddEdges + NUM_KINDS_OF_GROUPS );
        pBNS->vert[i + 1].iedge = pBNS->vert[i].iedge + k;
    }
    pBNS->num_atoms = num_atoms;      /* number of real atoms */
    pBNS->num_added_atoms = 0;
    pBNS->num_t_groups = 0;              /* number of added t-groups */
    pBNS->num_c_groups = 0;
    pBNS->nMaxAddAtoms = nMaxAddAtoms;
    pBNS->nMaxAddEdges = nMaxAddEdges;

    pBNS->num_vertices = num_atoms;     /*  current number of vertices, a sum of
                                        pBNS->num_atoms
                                        pBNS->num_t_groups
                                        pBNS->num_added_atoms           */
    pBNS->max_vertices = max_vertices;


    pBNS->num_bonds = num_bonds;        /* number of real edges (bonds)         */
    pBNS->max_edges = max_edges;
    pBNS->max_iedges = max_iedges;

    /*
    To remove t-groups and added atoms:
    In atoms i = 0..pBNS->num_atoms-1
    pBNS->vert[i].num_adj_edges = pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS;
    pBNS->num_vertices    = pBNS->num_atoms;
    pBNS->num_edges       = pBNS->num_bonds;
    pBNS->num_added_atoms = 0;
    pBNS->num_t_groups    = 0;
    pBNS->num_added_edges = 0;

    ALTP_DELTA(pBNS->alt_path)      = 0;
    ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX;
    ALTP_END_ATOM(pBNS->alt_path)   = NO_VERTEX;
    ALTP_PATH_LEN(pBNS->alt_path)   = 0;
    */

    /* Fill edges and connectivity */
    tot_st_cap = tot_st_flow = 0;
    for (i = 0, n_edges = 0; i < num_atoms; i++)
    {
        vert = &pBNS->vert[i];
        /* djb-rwth: removing redundant code */
        st_flow = 0;
        /* djb-rwth: removing redundant code */
        for (j = 0; j < at[i].valence; j++)
        {
            neigh = at[i].neighbor[j];
            /* find this bond at the neighbor */
            for (k = 0; k < at[neigh].valence; k++)
            {
                if (at[neigh].neighbor[k] == i)
                {
                    break;
                }
            }
            bond_type = ( at[i].bond_type[j] & BOND_TYPE_MASK );
            bond_mark = ( at[i].bond_type[j] & ~BOND_TYPE_MASK );
            if (bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE &&
                 bond_type != BOND_TRIPLE /*&& bond_type != BOND_ALTERN*/)
            {
                /* Make Unknown or Alternating bonds single */
                bond_type = 1;
                at[i].bond_type[j] = bond_mark | bond_type;
                num_changed_bonds++;
            }
            if (neigh > i)
            {
                /* This is the first time we encounter this bond */
                f1 = MAX_AT_FLOW( at[i] );
                f2 = MAX_AT_FLOW( at[neigh] );
                edge_flow = bond_type - 1;
                if (edge_flow > MAX_BOND_EDGE_CAP)
                {
                    /* djb-rwth: removing redundant code */
                    edge_flow = 0;  /* BNS will determine flows (that is, bonds) */
                    edge_cap = AROM_BOND_EDGE_CAP;
                }
                else
                {
#if ( 0 && KETO_ENOL_TAUT == 1 )  /* ????? */
                    edge_cap = inchi_max( f1, f2 );
#else
                    edge_cap = inchi_min( f1, f2 );
#endif
                    edge_cap = inchi_min( edge_cap, MAX_BOND_EDGE_CAP ); /* max capacity = 2 means up to triple bond */
                }

                pBNS->edge[n_edges].neighbor1 = (AT_NUMB) i;
                pBNS->edge[n_edges].neighbor12 = (AT_NUMB) ( i ^ neigh );
                pBNS->edge[n_edges].flow = pBNS->edge[n_edges].flow0 = edge_flow;
                pBNS->edge[n_edges].cap = pBNS->edge[n_edges].cap0 = edge_cap;
                pBNS->edge[n_edges].neigh_ord[0] = j;
                pBNS->edge[n_edges].neigh_ord[1] = k;
                pBNS->edge[n_edges].pass = 0;
                pBNS->edge[n_edges].forbidden = 0;

                vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges++;
            }
            else
            {
                /* This is the second time we encounter this bond. It was stored at */
                int  iedge = pBNS->vert[neigh].iedge[k];
                edge_cap = pBNS->edge[iedge].cap; /* djb-rwth: ignoring LLVM warning: variable used */
                edge_flow = pBNS->edge[iedge].flow;
            }
            st_flow += edge_flow;
            /* djb-rwth: removing redundant code */
        }
        vert->num_adj_edges = j;
        vert->st_edge.cap =
            vert->st_edge.cap0 = MAX_AT_FLOW( at[i] );
        vert->st_edge.flow =
            vert->st_edge.flow0 = st_flow;
        vert->type = BNS_VERT_TYPE_ATOM;
        tot_st_cap += vert->st_edge.cap;
        tot_st_flow += vert->st_edge.flow;
    }
    *pNum_changed_bonds = num_changed_bonds / 2;

    pBNS->num_edges = n_edges;   /* number of edges */
    pBNS->num_added_edges = 0;

    /*(@nnuk : Nauman Ullah Khan) */
    LOG_NO_ARGS("\n################# (L9108:ichi_bns.c) ################\n");
    for (i = 0, num_bonds = 0; i < num_atoms; i++) {

        LOG_MULT_ARGS("Element : %s, Number of changed bonds (End): %d\n", at[i].elname, *pNum_changed_bonds);
    }
    LOG_NO_ARGS("\n#####################################################\n");

    pBNS->tot_st_cap = tot_st_cap;
    pBNS->tot_st_flow = tot_st_flow;

    return pBNS;
}


/****************************************************************************/
BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS )
{
    int i;
    if (pBNS)
    {
        if (pBNS->edge)
        {
            inchi_free( pBNS->edge );
        }
        for (i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP; i++)
        {
            if (pBNS->altp[i])
            {
                inchi_free( pBNS->altp[i] );
            }
        }
        if (pBNS->vert)
        {
            if (pBNS->vert[0].iedge)
            {
                inchi_free( pBNS->vert[0].iedge );
            }
            inchi_free( pBNS->vert );
        }
        inchi_free( pBNS );
    }

    return NULL;
}


/****************************************************************************/
int ReInitBnStructAltPaths( BN_STRUCT *pBNS )
{
    int i;
    for (i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP; i++)
    {
        if (pBNS->altp[i])
        {
            ALTP_DELTA( pBNS->altp[i] ) = 0;
            ALTP_PATH_LEN( pBNS->altp[i] ) = 0;
            ALTP_START_ATOM( pBNS->altp[i] ) = NO_VERTEX;
            ALTP_END_ATOM( pBNS->altp[i] ) = NO_VERTEX;
        }
    }
    pBNS->alt_path = NULL;
    pBNS->num_altp = 0;
    return i;
}


/****************************************************************************/
int ReInitBnStructAddGroups( CANON_GLOBALS *pCG,
                             BN_STRUCT *pBNS,
                             inp_ATOM *at,
                             int num_atoms,
                             T_GROUP_INFO *tgi,
                             C_GROUP_INFO *cgi )
{
    int ret;
    /* strip all t-groups and c-groups */
    ret = ReInitBnStruct( pBNS, at, num_atoms, 0 );
    if (ret)
    {
        ret = BNS_REINIT_ERR;
        goto exit_function;
    }
    /*#if ( MOVE_CHARGES == 1 )*/
    if (*pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES)
    {
        /* Add c-groups */
        ret = AddCGroups2BnStruct( pCG, pBNS, at, num_atoms, cgi );
        if (IS_BNS_ERROR( ret ))
        {
            goto exit_function;
        }
    }
    /*#endif*/
    /* Add t-groups */
    ret = AddTGroups2BnStruct( pCG, pBNS, at, num_atoms, tgi );
    if (IS_BNS_ERROR( ret ))
    {
        goto exit_function;
    }

exit_function:

    return ret;
}


/****************************************************************************/
int ReInitBnStruct( BN_STRUCT *pBNS,
                    inp_ATOM *at,
                    int num_at,
                    int bRemoveGroupsFromAtoms )
{
    int i, vfict, kfict, iedgefict, endpoint, centerpoint, iedge, k;
    int ret = 0;
    if (pBNS)
    {
        if (pBNS->vert && pBNS->edge)
        {
            /* Debug */
            for (k = 0, i = 0; k < pBNS->num_edges; k++)
            {
                if (pBNS->edge[k].pass)
                {
                    i++;
                }
            }
            ret += i * 100;
            /* Restore flow and cap on edges to vertices connected to fictitious atoms */
            for (vfict = pBNS->num_atoms; vfict < pBNS->num_vertices; vfict++)
            {
                for (kfict = 0; kfict < pBNS->vert[vfict].num_adj_edges; kfict++)
                {
                    iedgefict = pBNS->vert[vfict].iedge[kfict]; /* fictitious edge to the endpoint */
                    endpoint = pBNS->edge[iedgefict].neighbor12 ^ vfict;  /* the endpoint */
                                                                          /* To simlify restore cap and flow in ALL edges to the endpoint */
                    if (bRemoveGroupsFromAtoms && endpoint < num_at)
                    {
                        at[endpoint].c_point = 0;
                        at[endpoint].endpoint = 0;
                    }
                    for (k = 0; k < pBNS->vert[endpoint].num_adj_edges; k++)
                    {
                        iedge = pBNS->vert[endpoint].iedge[k]; /* edge to endpoint */
                        centerpoint = pBNS->edge[iedge].neighbor12 ^ endpoint;
                        pBNS->edge[iedge].cap = pBNS->edge[iedge].cap0;
                        pBNS->edge[iedge].flow = pBNS->edge[iedge].flow0;
                        pBNS->edge[iedge].pass = 0;
#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
                        pBNS->edge[iedge].forbidden &= pBNS->edge_forbidden_mask;
#endif
                        pBNS->vert[centerpoint].st_edge.cap = pBNS->vert[centerpoint].st_edge.cap0;
                        pBNS->vert[centerpoint].st_edge.flow = pBNS->vert[centerpoint].st_edge.flow0;
                    }
                    pBNS->vert[endpoint].st_edge.cap = pBNS->vert[endpoint].st_edge.cap0;
                    pBNS->vert[endpoint].st_edge.flow = pBNS->vert[endpoint].st_edge.flow0;
                    pBNS->vert[endpoint].type &= BNS_VERT_TYPE_ATOM;
                }
            }
            /* Reset number of neighbors */
            if (pBNS->num_edges > pBNS->num_bonds)
            {
                for (i = 0; i < pBNS->num_atoms; i++)
                {
                    pBNS->vert[i].num_adj_edges =
                        pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS;
                }
            }
        }
        else
        {
            ret += 2;
        }
        if (!pBNS->edge)
        {
            ret += 4;
        }
        if (!pBNS->iedge)
        {
            ret += 8;
        }

        ReInitBnStructAltPaths( pBNS );

        pBNS->num_vertices = pBNS->num_atoms;
        pBNS->num_edges = pBNS->num_bonds;
        pBNS->num_added_atoms = 0;
        pBNS->num_t_groups = 0;
        pBNS->num_c_groups = 0;
        pBNS->num_added_edges = 0;
    }
    else
    {
        ret += 1;
    }

    return ret;
}


/****************************************************************************/
int CompTGroupNumber( const void *tg1, const void *tg2, void *p )
{
    return (int) ( (const T_GROUP *) tg1 )->nGroupNumber - (int) ( (const T_GROUP *) tg2 )->nGroupNumber;
}


/****************************************************************************/
int CompCGroupNumber( const void *cg1, const void *cg2, void *p )
{
    return (int) ( (const C_GROUP *) cg1 )->nGroupNumber - (int) ( (const C_GROUP *) cg2 )->nGroupNumber;
}


/****************************************************************************/
int AddTGroups2BnStruct( CANON_GLOBALS *pCG,
                         BN_STRUCT *pBNS,
                         inp_ATOM *at,
                         int num_atoms,
                         T_GROUP_INFO *tgi )
{
    int ret = 0;
    /* ret = ReInitBnStruct( pBNS ); */
    if (tgi && tgi->num_t_groups && tgi->t_group)
    {
        int         i, k, endpoint, centerpoint, fictpoint;
        int         num_tg = tgi->num_t_groups;
        int         num_edges = pBNS->num_edges;
        int         num_vertices = pBNS->num_vertices;
        BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev;  /* fictitious vertex describing t-group */
        BNS_VERTEX *vert_endpoint;
        BNS_EDGE   *edge;      /* edge between that vertex and the tautomeric endpoint */
        int        nMaxTGroupNumber = 0;
        ENDPOINT_INFO eif;

        /* Debug: check overflow */
        if (num_vertices + num_tg >= pBNS->max_vertices)
        {
            return BNS_VERT_EDGE_OVFL;
        }
        /* Find the largest t-group ID */
        for (i = 0; i < num_tg; i++)
        {
            if (tgi->t_group[i].nGroupNumber > nMaxTGroupNumber)
            {
                nMaxTGroupNumber = tgi->t_group[i].nGroupNumber;
            }
        }
        /* Since t-group IDs may be not contiguous, clear all vertices that will be added.
        all-zeroes-vertex will be ignored by the BNS
        */
        memset( pBNS->vert + num_vertices, 0, nMaxTGroupNumber * sizeof( pBNS->vert[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        /* Make sure the last t-group has the largest t-group ID:
        this is necessary to correctly add new edges
        and vertices for testing augmenting paths            */
#if ( bRELEASE_VERSION != 1 )
        insertions_sort( pCG, tgi->t_group, num_tg, sizeof( tgi->t_group[0] ), CompTGroupNumber );
        for (i = 1; i < num_tg; i++)
        {
            if (1 != tgi->t_group[i].nGroupNumber - tgi->t_group[i - 1].nGroupNumber)
            {
                return BNS_BOND_ERR;
            }
        }
#else
        if (nMaxTGroupNumber != tgi->t_group[num_tg - 1].nGroupNumber)
        {
            insertions_sort( pCG, tgi->t_group, num_tg, sizeof( tgi->t_group[0] ), CompTGroupNumber );
        }
#endif
        /* Initialize new fictitious vertices */
        ver_ficpont_prev = pBNS->vert + num_vertices - 1;

        for (i = 0; i < num_tg; i++, ver_ficpont_prev = vert_ficpoint)
        {
            /*
            vert_ficpoint-1 is the last vertex;
            vert_ficpoint   is the vertex that is being added
            Note: nGroupNumber are not contiguous
            */
            vert_ficpoint = pBNS->vert + num_vertices + tgi->t_group[i].nGroupNumber - 1;
            vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges;
            vert_ficpoint->max_adj_edges = tgi->t_group[i].nNumEndpoints + BNS_ADD_EDGES + BNS_ADD_SUPER_TGROUP;
            vert_ficpoint->num_adj_edges = 0;
            vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0;
            vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0;
            vert_ficpoint->type = BNS_VERT_TYPE_TGROUP;
        }

        for (endpoint = 0; endpoint < num_atoms; endpoint++)
        {
            if (!at[endpoint].endpoint)
            {
                continue;
            }
            fictpoint = at[endpoint].endpoint + num_vertices - 1;
            vert_ficpoint = pBNS->vert + fictpoint;
            vert_endpoint = pBNS->vert + endpoint;
            /* Debug: check overflow */
            if (fictpoint >= pBNS->max_vertices ||
                 num_edges >= pBNS->max_edges ||
                 vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges ||
                 vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges)
            {
                ret = BNS_VERT_EDGE_OVFL;
                break;
            }
            /* Obtain donor/acceptor info */
            if (!nGetEndpointInfo(at, endpoint, &eif)
#if ( TAUT_PT_22_00 == 1 )		 
                && !((tgi->bTautFlags & TG_FLAG_PT_22_00) && nGetEndpointInfo_PT_22_00(at, endpoint, &eif))
#endif
#if ( TAUT_PT_16_00 == 1 )		 
                && !((tgi->bTautFlags & TG_FLAG_PT_16_00) && nGetEndpointInfo_PT_16_00(at, endpoint, &eif))
#endif
#if ( TAUT_PT_06_00 == 1 )		 
                && !((tgi->bTautFlags & TG_FLAG_PT_06_00) && nGetEndpointInfo_PT_06_00(at, endpoint, &eif))
#endif
#if ( TAUT_PT_39_00 == 1 )		 
                && !((tgi->bTautFlags & TG_FLAG_PT_39_00) && nGetEndpointInfo_PT_39_00(at, endpoint, &eif))
#endif
#if ( TAUT_PT_13_00 == 1 )		 
                && !((tgi->bTautFlags & TG_FLAG_PT_13_00) && nGetEndpointInfo_PT_13_00(at, endpoint, &eif))
#endif
#if ( TAUT_PT_18_00 == 1 )		 
                && !((tgi->bTautFlags & TG_FLAG_PT_18_00) && nGetEndpointInfo_PT_18_00(at, endpoint, &eif))
#endif		 
                ) {
#if ( KETO_ENOL_TAUT == 1 )
                if (!( ( tgi->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) &&
                       nGetEndpointInfo_KET( at, endpoint, &eif ) ))
#endif
                {
                    ret = BNS_BOND_ERR;
                    break;
                }
            }

            vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT;

            /* Set capacity = 1 to the edges from the endpoint to the centerpoint(s) */
            for (k = 0; k < vert_endpoint->num_adj_edges; k++)
            {
                int iedge = vert_endpoint->iedge[k];
                if (!pBNS->edge[iedge].cap)
                {
                    /* Single bond, possibly between endpoint and centerpoint */
                    centerpoint = ( pBNS->edge[iedge].neighbor12 ^ endpoint );
                    if (centerpoint < pBNS->num_atoms &&
                         pBNS->vert[centerpoint].st_edge.cap >= 1)
                    {
                        int bond_type = ( at[endpoint].bond_type[k] & BOND_TYPE_MASK );
                        if (bond_type == BOND_TAUTOM ||
                             bond_type == BOND_ALTERN ||
                             bond_type == BOND_ALT12NS ||
                             bond_type == BOND_SINGLE)
                        {
                            pBNS->edge[iedge].cap = 1;
                        }
                    }
                }
            }

            /* Create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */
            edge = pBNS->edge + num_edges;
            edge->cap = 1;
            edge->flow = 0;
            edge->pass = 0;
#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
            edge->forbidden &= pBNS->edge_forbidden_mask;
#endif
            /* Later include case when the charge change allows the endpoint to become tautomeric */
            /* mark endoint having moveable H atom with flow=1 */

            /* -- old "no charges" version -- */
            /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */
            /* -- the following line takes charges into account -- */
            if (eif.cDonor) /* means the endpoint has an H-atom to donate */
            {
                /* increment edge flow */
                edge->flow++;
                /* increment one vertex st-flow & cap */
                vert_ficpoint->st_edge.flow++;
                vert_ficpoint->st_edge.cap++;
                /* increment another vertex st-flow & cap */
                vert_endpoint->st_edge.flow++;
                vert_endpoint->st_edge.cap++;
            }
            /* Connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */
            edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */
            edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */
            vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges;
            vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges++;
            edge->neigh_ord[0] = vert_endpoint->num_adj_edges++;
            edge->neigh_ord[1] = vert_ficpoint->num_adj_edges++;
            edge->cap0 = edge->cap;
            edge->flow0 = edge->flow;
        }

        pBNS->num_edges = num_edges;
        pBNS->num_vertices += nMaxTGroupNumber;
        pBNS->num_t_groups = num_tg;
    }

    return ret;
}


/*#if ( MOVE_CHARGES == 1 )*/ /* { */


                              /****************************************************************************/
int AddCGroups2BnStruct( CANON_GLOBALS *pCG,
                         BN_STRUCT *pBNS,
                         inp_ATOM *at,
                         int num_atoms,
                         C_GROUP_INFO *cgi )
{
    int ret = 0;
    /* ret = ReInitBnStruct( pBNS ); */
    if (cgi && cgi->num_c_groups && cgi->c_group)
    {
        int         i, k, c_point, centerpoint, fictpoint;
        int         num_cg = cgi->num_c_groups;
        int         num_edges = pBNS->num_edges;
        int         num_vertices = pBNS->num_vertices;
        BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev;  /* fictitious vertex describing charge c-group */
        BNS_VERTEX *vertex_cpoint;
        BNS_EDGE   *edge;      /* edge between that vertex and the tautomeric c_point */
        int        nMaxCGroupNumber = 0;

        /* Debug: check overflow */
        if (num_vertices + num_cg >= pBNS->max_vertices)
        {
            return BNS_VERT_EDGE_OVFL;
        }
        /* Find the largest t-group ID */
        for (i = 0; i < num_cg; i++)
        {
            if (cgi->c_group[i].nGroupNumber > nMaxCGroupNumber)
            {
                nMaxCGroupNumber = cgi->c_group[i].nGroupNumber;
            }
        }
        /* Since t-group IDs may be not contiguous, clear all vertices that will be added.
        all-zeroes-vertex will be ignored by the BNS
        */
        memset( pBNS->vert + num_vertices, 0, nMaxCGroupNumber * sizeof( pBNS->vert[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        /* Make sure the last t-group has the largest t-group ID:
        this is necessary to correctly add new edges and vertices for testing augmenting paths
        */
#if ( bRELEASE_VERSION != 1 )
        insertions_sort( cgi->c_group, num_cg, sizeof( cgi->c_group[0] ), CompCGroupNumber );
        for (i = 1; i < num_cg; i++)
        {
            if (1 != cgi->c_group[i].nGroupNumber - cgi->c_group[i - 1].nGroupNumber)
            {
                return BNS_BOND_ERR;
            }
        }
#else
        if (nMaxCGroupNumber != cgi->c_group[num_cg - 1].nGroupNumber)
        {
            insertions_sort( pCG, cgi->c_group, num_cg,
                             sizeof( cgi->c_group[0] ),
                             CompCGroupNumber );
        }
#endif
        /**************************************/
        /* initialize new fictitious vertices */
        /* representing c-point groups        */
        /**************************************/
        ver_ficpont_prev = pBNS->vert + num_vertices - 1;

        for (i = 0; i < num_cg; i++, ver_ficpont_prev = vert_ficpoint)
        {
            /*
            vert_ficpoint-1 is the last vertex;
            vert_ficpoint   is the being added vertex
            Note: nGroupNumber are not contiguous
            */
            vert_ficpoint = pBNS->vert + num_vertices + cgi->c_group[i].nGroupNumber - 1;
            vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges;
            vert_ficpoint->max_adj_edges = cgi->c_group[i].num_CPoints + BNS_ADD_EDGES;
            vert_ficpoint->num_adj_edges = 0;
            vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0;
            vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0;
            vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP;
        }

        /************************************************/
        /* Connect c-points to the fictitious vertices  */
        /* representing c-point groups; set caps, flows */
        /************************************************/
        for (c_point = 0; c_point < num_atoms; c_point++)
        {
            if (!at[c_point].c_point)
            {
                continue;
            }
            fictpoint = at[c_point].c_point + num_vertices - 1; /* c-group vertex index */
            vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */
            vertex_cpoint = pBNS->vert + c_point;   /* c_point vertex */
                                                    /* Debug: check overflow */
            if (fictpoint >= pBNS->max_vertices ||
                 num_edges >= pBNS->max_edges ||
                 vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges ||
                 vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges)
            {
                ret = BNS_VERT_EDGE_OVFL;
                break;
            }
            vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT;
#if ( FIX_CPOINT_BOND_CAP != 1 )  /* { */
            /* set capacity = 1 to the edges from the c_point to the centerpoint(s)     */
            /* if their current capacity is zero                                        */
            /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */
            for (k = 0; k < vertex_cpoint->num_adj_edges; k++)
            {
                int iedge = vertex_cpoint->iedge[k];
                if (!pBNS->edge[iedge].cap)
                {
                    /* single bond, possibly between c_point and centerpoint */
                    centerpoint = ( pBNS->edge[iedge].neighbor12 ^ c_point );
                    if (centerpoint < pBNS->num_atoms &&
                         pBNS->vert[centerpoint].st_edge.cap >= 1)
                    {
                        int bond_type = ( at[c_point].bond_type[k] & BOND_TYPE_MASK );
                        if (bond_type == BOND_TAUTOM ||
                             bond_type == BOND_ALTERN ||
                             bond_type == BOND_SINGLE)
                        {
                            pBNS->edge[iedge].cap = 1;
                        }
                    }
                }
            }
#endif /* } FIX_CPOINT_BOND_CAP */

            /* Create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */
            edge = pBNS->edge + num_edges;
            edge->cap = 1;
            edge->flow = 0;
            edge->pass = 0;
#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
            edge->forbidden &= pBNS->edge_forbidden_mask;
#endif
            /* Mark edge to c-point having NO moveable charge with flow=1 */
            if (!CHARGED_CPOINT( at, c_point ))
            {
                /* Increment edge flow */
                edge->flow++;
                /* Increment c-group vertex st-flow & cap */
                vert_ficpoint->st_edge.flow++;
                vert_ficpoint->st_edge.cap++;
                /* Increment c-point vertex st-flow & cap */
                vertex_cpoint->st_edge.flow++;
                vertex_cpoint->st_edge.cap++;
            }

#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */

            /* Set capacity = 1 to the edges from the c_point to the centerpoint(s)     */
            /* if their current capacity is zero                                        */
            /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */
            for (k = 0; k < vertex_cpoint->num_adj_edges; k++)
            {
                int iedge = vertex_cpoint->iedge[k];
                VertexFlow  nNewCap = vertex_cpoint->st_edge.cap;
                centerpoint = ( pBNS->edge[iedge].neighbor12 ^ c_point );
                if (!pBNS->edge[iedge].cap)
                {
                    /* Single bond, possibly between c_point and centerpoint */
                    if (centerpoint < pBNS->num_atoms &&
                         pBNS->vert[centerpoint].st_edge.cap >= 1)
                    {
                        nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap );
                        nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP );
                        pBNS->edge[iedge].cap = nNewCap;
                    }
#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */
                    else
                        if (centerpoint < pBNS->num_atoms &&
                             edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP)
                        {
                            pBNS->edge[iedge].cap++;
                        }
#endif
                }
            }
#endif  /* } FIX_CPOINT_BOND_CAP */

            /* Connect edge to c_point and fictpoint and increment the counters of neighbors and edges */
            edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */
            edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */
            vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges;
            vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges++;
            edge->neigh_ord[0] = vertex_cpoint->num_adj_edges++;
            edge->neigh_ord[1] = vert_ficpoint->num_adj_edges++;
            edge->cap0 = edge->cap;
            edge->flow0 = edge->flow;
        }

        pBNS->num_edges = num_edges;
        pBNS->num_vertices += nMaxCGroupNumber;
        pBNS->num_c_groups = num_cg;
    }

    return ret;
}
/*#endif*/ /* } MOVE_CHARGES == 1 */



           /****************************************************************************/
void ClearAllBnDataVertices( Vertex *v, Vertex value, int size )
{
    int i;
    for (i = 0; i < size; i++)
    {
        v[i] = value;
    }
}


/****************************************************************************/
void ClearAllBnDataEdges( Edge *e, Vertex value, int size )
{
    int i;
    for (i = 0; i < size; i++)
    {
        e[i][0] = value;
    }
}


/****************************************************************************/
BN_DATA *DeAllocateBnData( BN_DATA *pBD )
{
    if (pBD)
    {
        if (pBD->BasePtr)
        {
            inchi_free( pBD->BasePtr );
        }
        if (pBD->SwitchEdge)
        {
            inchi_free( pBD->SwitchEdge );
        }
        if (pBD->Tree)
        {
            inchi_free( pBD->Tree );
        }
        if (pBD->ScanQ)
        {
            inchi_free( pBD->ScanQ );
        }
        if (pBD->Pu)
        {
            inchi_free( pBD->Pu );
        }
        if (pBD->Pv)
        {
            inchi_free( pBD->Pv );
        }
#if ( BNS_RAD_SEARCH == 1 )
        if (pBD->RadEndpoints)
        {
            inchi_free( pBD->RadEndpoints );
        }
        if (pBD->RadEdges)
        {
            inchi_free( pBD->RadEdges );
        }
#endif
        inchi_free( pBD );
    }

    return NULL;
}


/****************************************************************************/
BN_DATA *AllocateAndInitBnData( int max_num_vertices )
{
    BN_DATA *pBD = NULL;
    int      max_len_Pu_Pv;
    max_num_vertices = 2 * max_num_vertices + 2;
    max_len_Pu_Pv = max_num_vertices / 2 + 1;
    max_len_Pu_Pv += max_len_Pu_Pv % 2; /* even length */
    if (!( pBD = (BN_DATA *) inchi_calloc( 1, sizeof( BN_DATA ) ) ) ||
         !( pBD->BasePtr = (Vertex *) inchi_calloc( max_num_vertices, sizeof( Vertex ) ) ) ||
         !( pBD->SwitchEdge = (Edge   *) inchi_calloc( max_num_vertices, sizeof( Edge ) ) ) ||
         !( pBD->Tree = (S_CHAR *) inchi_calloc( max_num_vertices, sizeof( S_CHAR ) ) ) ||
         !( pBD->ScanQ = (Vertex *) inchi_calloc( max_num_vertices, sizeof( Vertex ) ) ) ||
         !( pBD->Pu = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof( Vertex ) ) ) ||
#if ( BNS_RAD_SEARCH == 1 )
         !( pBD->RadEndpoints = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof( Vertex ) ) ) ||
         !( pBD->RadEdges = (EdgeIndex*) inchi_calloc( max_len_Pu_Pv, sizeof( EdgeIndex ) ) ) ||
#endif
         !( pBD->Pv = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof( Vertex ) ) )
         )
    {
        pBD = DeAllocateBnData( pBD );
    }
    else
    {
        /* Initialize data */
        ClearAllBnDataEdges( pBD->SwitchEdge, NO_VERTEX, max_num_vertices );
        ClearAllBnDataVertices( pBD->BasePtr, NO_VERTEX, max_num_vertices );
        memset( pBD->Tree, TREE_NOT_IN_M, max_num_vertices ); /* djb-rwth: memset_s C11/Annex K variant? */
        pBD->QSize = -1;
        pBD->max_len_Pu_Pv = max_len_Pu_Pv;
        pBD->max_num_vertices = max_num_vertices;
#if ( BNS_RAD_SEARCH == 1 )
        pBD->nNumRadEndpoints = 0;
#endif
    }

    return pBD;
}


/****************************************************************************/
int ReInitBnData( BN_DATA *pBD )
{
    int i, ret = 0;
    Vertex u, v;
    if (pBD)
    {
        if (!pBD->ScanQ)
        {
            ret += 2;
        }
        if (!pBD->BasePtr)
        {
            ret += 4;
        }
        if (!pBD->SwitchEdge)
        {
            ret += 8;
        }
        if (!pBD->Tree)
        {
            ret += 16;
        }
        if (!ret)
        {
            for (i = 0; i <= pBD->QSize; i++)
            {
                u = pBD->ScanQ[i];
                v = prim( u );
                pBD->BasePtr[u] =
                    pBD->BasePtr[v] =
                    pBD->SwitchEdge_Vert1( u ) =
                    pBD->SwitchEdge_Vert1( v ) = NO_VERTEX;
                pBD->Tree[u] =
                    pBD->Tree[v] = TREE_NOT_IN_M;
            }
        }
        pBD->QSize = -1;
        if (!pBD->Pu)
        {
            ret += 32;
        }
        if (!pBD->Pv)
        {
            ret += 64;
        }
    }
    else
    {
        ret += 1;
    }

    return ret;
}


/****************************************************************************/
int GetVertexDegree( BN_STRUCT* pBNS, Vertex v )
{
    int i = v / 2 - 1;
    if (i >= 0)
    {
        if (pBNS->vert[i].st_edge.cap > 0)
        {
            return pBNS->vert[i].num_adj_edges + 1; /* add 1 neighbor for s or t */
        }
        else
        {
            return 0;   /* since the edge s-v has zero capacity, we ignore vertex v */
        }
    }
    else
    {
        return pBNS->num_vertices;
    }
}


/****************************************************************************/
Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv )
{
    /*
    Vertex ret;
    */
    if (uv[1] >= 0)
    {
        /* -- debug --
        if ( uv[1] > pBNS->num_edges || uv[0] > 2*pBNS->num_vertices+3 ) {
        int stop = 1;
        }
        ret = ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2;
        if ( ret > 2*pBNS->num_vertices+3 ) {
        int stop = 1;
        }
        return ret;
        -- end debug -- */

        return ( ( uv[0] - 2 ) ^ ( 2 * pBNS->edge[uv[1]].neighbor12 + 1 ) ) + 2;

        /*short u = uv[0]-FIRST_INDX; */
        /*short t = 2*(((u / 2 - 1) ^ pBNS->edge[uv[1]].neighbor12) + 1) + ((u+1) & 1) + FIRST_INDX; */
        /*return t; */
    }

    if (uv[0] <= 1)
    {
        return -( 1 + uv[1] ); /* vertex1 is s or t, return x or y */
    }
    else
    {
        return uv[0] % 2; /* vertex1 is x or y, return s or t; never happens? -- NSC 3737, 7634,... */
    }

}


/****************************************************************************/
Vertex GetVertexNeighbor( BN_STRUCT* pBNS,
                          Vertex v,
                          int neigh,
                          EdgeIndex *iedge )
{
    /* neigh = 0 => the neighbor is s or t except case when v is s or t. */
    /* v= FIRST_INDX or FIRST_INDX+1: v is s or t respectively */
    int i, neighbor;
    if (( i = v - 2 ) >= 0)
    {
        /* Neighbor of x or y */
        if (neigh)
        {
            neigh--;
            /* x or y */
            *iedge = pBNS->vert[i / 2].iedge[neigh];
            if (!( pBNS->edge[*iedge].cap & EDGE_FLOW_MASK ) || IS_FORBIDDEN( pBNS->edge[*iedge].forbidden, pBNS ))
            {
                return NO_VERTEX;
            }
            neighbor = ( i ^ ( 2 * pBNS->edge[*iedge].neighbor12 + 1 ) ) + 2; /* parity opposite to v parity */
        }
        else
        {
            /* neighbor of x or y is s or t */
            neighbor = ( v & 1 ); /* s or t, same parity as v */
            *iedge = -( neighbor + 1 );
        }
    }
    else
    {
        /* neighbor of s or t: x or y, same parity as v */
        if (!( pBNS->vert[neigh].st_edge.cap & EDGE_FLOW_ST_MASK ))
        {
            return NO_VERTEX;
        }
        neighbor = 2 * neigh + 2 + ( v & 1 ); /* parity same as the parity of v */
        *iedge = -( neighbor + 1 );
    }

    return neighbor;
}


/****************************************************************************/
int GetEdgePointer( BN_STRUCT* pBNS,
                    Vertex u,
                    Vertex v,
                    EdgeIndex iuv,
                    BNS_EDGE **uv,
                    S_CHAR *s_or_t )
{
    int i = u / 2 - 1;
    int j = v / 2 - 1;
    int bBackward = BNS_WRONG_PARMS;
    *uv = NULL;
    if (i >= 0)
    {
        /* u is an atom */
        if (j >= 0)
        {
            /* v is an atom */
            if (( u + v ) % 2)
            {
                *uv = pBNS->edge + iuv;
                bBackward = ( u & 1 );
                *s_or_t = 0;
            }
        }
        else
        {
            /* v is s or t */
            if (v >= 0 && !( ( u + v ) % 2 ))
            {
                *uv = (BNS_EDGE*) &pBNS->vert[i].st_edge;
                bBackward = !( v & 1 );
                *s_or_t = v + 3; /* 3=> v=s, 4=> v=t */
            }
        }
    }
    else
    {
        if (j >= 0)
        {
            /* u is s or t */
            if (u >= 0 && !( ( u + v ) % 2 ))
            {
                /* v is an atom */
                *uv = (BNS_EDGE*) &pBNS->vert[j].st_edge;
                bBackward = ( u & 1 );
                *s_or_t = u + 1; /* 1=> u=s, 2=> u=t */
            }
        }
    }

    return bBackward;
}


/****************************************************************************/
int AugmentEdge( BN_STRUCT* pBNS,
                 Vertex u,
                 Vertex v,
                 EdgeIndex iuv,
                 int delta,
                 S_CHAR bReverse,
                 int bChangeFlow )
{
    int f, flow, ret = 0;

    BNS_ST_EDGE    *pst_edge;
    BNS_EDGE       *pedge;
    S_CHAR          s_or_t;
    int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t );
    if (!IS_BNS_ERROR( bBackward ))
    {
        if (bBackward)
        {
            delta = -delta;
        }

        if (s_or_t)
        {
            pst_edge = (BNS_ST_EDGE *) pedge;
            flow = pst_edge->flow;
            f = ( flow & EDGE_FLOW_ST_MASK ) + delta; /* new flow */
            if (!delta)
            {
                /*((BNS_ST_EDGE *)pedge)->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH;*/
                pst_edge->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH;
            }
            else
            {
                int cap = pst_edge->cap;
                if (f < 0 || f > cap)
                {
                    ret = BNS_WRONG_PARMS;
                }
                else
                {
                    if (!( bChangeFlow & BNS_EF_CHNG_FLOW ))
                    {
                        f -= delta; /* do not actually change the flow, only find the augmenting path */
                    }
                    else
                    {
                        if (delta)
                        {
                            /*((BNS_ST_EDGE *)pedge)->pass ++;*/
                            pst_edge->pass++;
                        }
                    }
                    flow = ( flow & ~( EDGE_FLOW_ST_PATH | EDGE_FLOW_ST_MASK ) ) + f;
                    /*((BNS_ST_EDGE *)pedge)->flow = flow;*/
                    pst_edge->flow = flow;
                    /*((BNS_ST_EDGE *)pedge)->delta += delta; */
                    if (bReverse)
                    {
                        /* u <- v; Note: in case of bReverse s_or_t has actually been determined
                        for the u' <- v' pair; therefore s and t should be switched
                        in order to correctly determine the 1st or the last atom
                        on the augmenting path.
                        */
                        switch (s_or_t)
                        {
                            case 1: /* u = t: t<-v, v is the last vertex */
                                ALTP_END_ATOM( pBNS->alt_path ) = v / 2 - 1;
                                break;
                            case 2: /* u = s: s<-v, error */
                                ret = BNS_WRONG_PARMS;
                                break;
                            case 3: /* v = t: u<-t, error */
                                ret = BNS_WRONG_PARMS;
                                break;
                            case 4: /* v = s: u<-s, u is the first vertex */
                                ALTP_START_ATOM( pBNS->alt_path ) = u / 2 - 1;
                                ALTP_DELTA( pBNS->alt_path ) = delta;
                                break;
                            default:
                                ret = BNS_WRONG_PARMS;
                                break;
                        }
                    }
                    else
                    {
                        /* u -> v */
                        switch (s_or_t)
                        {
                            case 1: /* u = s: s->v, v is the first vertex */
                                ALTP_START_ATOM( pBNS->alt_path ) = v / 2 - 1;
                                ALTP_DELTA( pBNS->alt_path ) = delta;
                                break;
                            case 2: /* u = t: t->v, error */
                                ret = BNS_WRONG_PARMS;
                                break;
                            case 3: /* v = s: u->s, error */
                                ret = BNS_WRONG_PARMS;
                                break;
                            case 4: /* v = t: u->t, u is the last vertex */
                                ALTP_END_ATOM( pBNS->alt_path ) = u / 2 - 1;
                                break;
                            default:
                                ret = BNS_WRONG_PARMS;
                                break;
                        }
                    }
                }
            }
        }
        else
        {
            f = ( pedge->flow & EDGE_FLOW_MASK ) + delta;
            if (!delta)
            {
                pedge->flow &= ~EDGE_FLOW_PATH;
            }
            else
            {
                if (f < 0 || f > pedge->cap)
                {
                    ret = BNS_WRONG_PARMS;
                }
                else
                {
                    AT_NUMB   iu = u / 2 - 1;
                    AT_NUMB   iv = v / 2 - 1;
                    int       indx;
                    if (!( bChangeFlow & BNS_EF_CHNG_FLOW ))
                    {
                        f -= delta; /* do not actually change the flow, only find the augmenting path */
                    }
                    else
                    {
                        if (delta)
                        {
                            pedge->pass++;
                        }
                    }
                    pedge->flow = ( pedge->flow & ~( EDGE_FLOW_PATH | EDGE_FLOW_MASK ) ) | f;
                    if (ALTP_MAY_ADD( pBNS->alt_path ))
                    {
                        /* bReverse? u <- v : u -> v */
                        indx = bReverse ? ( pedge->neighbor1 == iv ) : ( pedge->neighbor1 == iu );
                        ALTP_CUR_THIS_ATOM_NEIGHBOR( pBNS->alt_path ) = pedge->neigh_ord[1 - indx];
                        ALTP_CUR_NEXT_ATOM_NEIGHBOR( pBNS->alt_path ) = pedge->neigh_ord[indx];
                        ALTP_NEXT( pBNS->alt_path );
                    }
                    else
                    {
                        ALTP_OVERFLOW( pBNS->alt_path ) = 1;
                        ret = BNS_ALTPATH_OVFL;
                    }
                }
            }
        }
        return ret ? ret : f;
    }

    return bBackward;
}


/****************************************************************************
rescap_mark( ... )

Find residual capacity and mark the edge
as belonging to the augmenting path
****************************************************************************/
int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv )
{
    BNS_ST_EDGE    *pst_edge;
    BNS_EDGE       *pedge;

    int    f, flow;
    S_CHAR s_or_t;
    int    bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t );

    if (!IS_BNS_ERROR( bBackward ))
    {
        if (s_or_t)
        {
            pst_edge = (BNS_ST_EDGE *) pedge;
            flow = pst_edge->flow;
            f = ( flow & EDGE_FLOW_ST_MASK );
            if (!bBackward)
            {
                f = (int) pst_edge->cap - f;
            }
            if (flow & EDGE_FLOW_ST_PATH)
            {
                pBNS->bNotASimplePath++;
                f /= 2;   /* this is the second time we pass the same edge: reduce flow by a factor of 2 */
            }
            else
            {
                pst_edge->flow |= EDGE_FLOW_ST_PATH; /* mark the edge */
            }
        }
        else
        {
            flow = pedge->flow;
            f = flow & EDGE_FLOW_MASK;
            if (!bBackward)
            {
                f = (int) pedge->cap - f;
            }
            if (flow & EDGE_FLOW_PATH)
            {
                f /= 2;    /* this is the second time we pass the same edge: reduce flow by a factor of 2 */
                pBNS->bNotASimplePath++;
            }
            else
            {
                pedge->flow |= EDGE_FLOW_PATH;  /* mark the edge */
            }
        }
        return f;
    }

    return bBackward;
}


/****************************************************************************
GetPrevVertex( ... )

Get previous vertex in the searched path
z is SwitchEdge_Vert2(y) != y. Go backward from z to y
****************************************************************************/
Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv )
{
    Vertex w, z, x2, y2; /* djb-rwth: removing redundant variables */
    EdgeIndex iwy;

    w = SwitchEdge_Vert1( y );
    z = SwitchEdge_Vert2( y );
    iwy = SwitchEdge_IEdge( y );
    if (z == y)
    {
        *iuv = iwy;
        return w;
    }
    x2 = prim( y );
    y2 = prim( z );
    /* djb-rwth: removing redundant code */
    while (y2 != NO_VERTEX)
    {
        w = SwitchEdge_Vert1( y2 );
        z = SwitchEdge_Vert2( y2 );
        iwy = SwitchEdge_IEdge( y2 );
        if (w == x2)
        {
            *iuv = iwy;
            /*return z; */
            return ( y + z ) % 2 ? z : prim( z );
        }
        /* djb-rwth: removing redundant code */
        /*
        #ifdef _DEBUG
        if ( n ) {
        int stop = 1;
        }
        #endif
        */
        if (w == y2)
        {
            return NO_VERTEX;
        }
        y2 = w;
    }

    return y2;
}



#define CHECK_TACN  1


/****************************************************************************
The purpose is to avoid paths

(H-group)[u]---atom[v]---((-)-cgroup)[w],

where atom[v] is not acidic and (-) and H are not interchangeable without
explicit bond tautomerism.

It is important that acidic atoms are only O,S,Se,Te and should have
only one chemical bond. Only because of this an early rejection of
the vertex v (before it gets on SCANQ) is possible.

CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N
along an alternating path from another heteroatom (t-group will be detected).
****************************************************************************/


#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */


/****************************************************************************/
int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
{
#define TYPE_T   1   /* t-group [also called H-group] */
#define TYPE_CN  2   /* (-)c-group */
    int    i, degree, ret, u_is_taut = 0, w_is_taut, num_allowed = 0, num_found_groups = 0;
    Vertex w;
    EdgeIndex ivw;
    if (!pBNS->type_TACN || u <= 1 || v <= 1 ||
        ( pBNS->vert[v / 2 - 1].type & pBNS->type_TACN ))
    {
        return 0; /* add/remove H(+) is allowed for acidic atoms */
    }
    if (!pBNS->type_T || !pBNS->type_CN)
    {
        return 0; /* should not happen */
    }
    u_is_taut = ( ( pBNS->vert[u / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ? TYPE_T :
        ( ( pBNS->vert[u / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) ? TYPE_CN : 0;
    if (u_is_taut)
    {
        /* u is either t-group vertex or (-) c-group */
        degree = GetVertexDegree( pBNS, v );
        for (i = 0; i < degree; i++)
        {
            /* v = vert[u].neighbor[i]; */
            w = GetVertexNeighbor( pBNS, v, i, &ivw );
            if (w == NO_VERTEX || w <= 1)
            {
                continue; /* the atom has only single bonds or it is s or t, ignore it */
            }
            if (w != u && ( ret = rescap( pBNS, v, w, ivw ) ) > 0)
            {
                num_allowed++;
                w_is_taut = ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) ? TYPE_CN :
                    ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ? TYPE_T : 0;
                if (( u_is_taut | w_is_taut ) == ( TYPE_T | TYPE_CN ))
                {
                    num_found_groups++;
                }
            }
        }
        if (num_found_groups && num_allowed == 1)
        {
            return 1; /* reject */
        }
    }

    return 0;
#undef TYPE_T
#undef TYPE_CN
}


#else  /* } FIX_TACN_POSSIBLE_BUG { */


/****************************************************************************/
int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
{
    int    i, degree, ret, u_is_taut = 0, num_allowed = 0, num_found_groups = 0;
    Vertex w;
    EdgeIndex ivw;
    if (!pBNS->type_TACN || u <= 1 || v <= 1 ||
        ( pBNS->vert[v / 2 - 1].type & pBNS->type_TACN ))
    {
        return 0; /* add/remove H(+) is allowed for acidic atoms */
    }
    if (!pBNS->type_T || !pBNS->type_CN)
    {
        return 0; /* should not happen */
    }
    if (( u_is_taut = ( pBNS->vert[u / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ||
        ( ( pBNS->vert[u / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ))
    {
        /* u is either t-group vertex or (-) c-group */
        degree = GetVertexDegree( pBNS, v );
        for (i = 0; i < degree; i++)
        {
            /* v = vert[u].neighbor[i]; */
            w = GetVertexNeighbor( pBNS, v, i, &ivw );
            if (w == NO_VERTEX || w <= 1)
            {
                continue; /* the atom has only single bonds or it is s or t, ignore it */
            }
            if (w != u && ( ret = rescap( pBNS, v, w, ivw ) ) > 0) /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
            {
                num_allowed++;
                if (( u_is_taut ? ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) :
                    ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ))
                {
                    num_found_groups++;
                }
            }
        }
        if (num_found_groups && num_allowed == 1)
        {
            return 1; /* reject */
        }
    }

    return 0;
}



#endif /* } FIX_TACN_POSSIBLE_BUG */



/****************************************************************************
The purpose is to avoid paths
(H-group)[u]---atom[v]---((-)-cgroup)[w],

where atom[v] is not acidic and (-) and H are not interchangeable without
explicit bond tautomerism.

It is important that acidic atoms are only O,S,Se,Te and should have
only one chemical bond. Only because of this an early rejection of
the vertex v (before it gets on SCANQ) is possible.
****************************************************************************/


#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */


/****************************************************************************/
int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS,
                                Vertex v,
                                Vertex w,
                                Edge *SwitchEdge )
{
#define TYPE_T   1   /* t-group [also called H-group] */
#define TYPE_CN  2   /* (-)c-group */
    int    u_is_taut = 0, w_is_taut = 0;
    Vertex u;
    EdgeIndex iuv;
    if (v <= 1 || w <= 1)
        return 0;
#if ( CHECK_TACN == 1 )
    if (!pBNS->type_TACN ||
        ( pBNS->vert[v / 2 - 1].type & pBNS->type_TACN ))
    {
        return 0;
    }
    if (!pBNS->type_T || !pBNS->type_CN)
    {
        return 0; /* should not happen */
    }
#endif
    u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv );
    /*
    u   = SwitchEdge_Vert1(v);
    iuv = SwitchEdge_IEdge(v);
    */
    if (u == NO_VERTEX || iuv < 0)
        return 0; /* should not happen */
                  /* check edge adjacency */
    if (pBNS->edge[iuv].neighbor1 != ( u / 2 - 1 ) && pBNS->edge[iuv].neighbor1 != v / 2 - 1 ||
        ( pBNS->edge[iuv].neighbor12 ^ ( u / 2 - 1 ) ) != ( v / 2 - 1 ))
    {
        return 0; /* !!! should not happen !!! */
    }

#if ( CHECK_TACN == 1 )
    u_is_taut = ( ( pBNS->vert[u / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ? TYPE_T :
        ( ( pBNS->vert[u / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) ? TYPE_CN : 0;
    w_is_taut = ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ? TYPE_T :
        ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) ? TYPE_CN : 0;
    if (( u_is_taut | w_is_taut ) == ( TYPE_T | TYPE_CN ))
    {
        /* rescap must have already been checked */
        return 1;
    }
#endif

    return 0;
#undef TYPE_T
#undef TYPE_CN
}


#else  /* } FIX_TACN_POSSIBLE_BUG { */


/****************************************************************************/
int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS,
                                Vertex v,
                                Vertex w,
                                Edge *SwitchEdge )
{
    int    u_is_taut = 0, w_is_taut = 0;
    Vertex u;
    EdgeIndex iuv;
    if (v <= 1 || w <= 1)
        return 0;
#if ( CHECK_TACN == 1 )
    if (!pBNS->type_TACN ||
        ( pBNS->vert[v / 2 - 1].type & pBNS->type_TACN ))
    {
        return 0;
    }
    if (!pBNS->type_T || !pBNS->type_CN)
    {
        return 0; /* should not happen */
    }
#endif
    u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv );
    /*
    u   = SwitchEdge_Vert1(v);
    iuv = SwitchEdge_IEdge(v);
    */
    if (u == NO_VERTEX || iuv < 0)
    {
        return 0; /* should not happen */
    }
    /* check edge adjacency */
    if ((pBNS->edge[iuv].neighbor1 != ( u / 2 - 1 ) && pBNS->edge[iuv].neighbor1 != v / 2 - 1) ||
        (( pBNS->edge[iuv].neighbor12 ^ ( u / 2 - 1 ) ) != ( v / 2 - 1 ))) /* djb-rwth: addressing LLVM warning */
    {
        return 0; /* !!! should not happen !!! */
    }

#if ( CHECK_TACN == 1 )
    if (( ( u_is_taut = ( pBNS->vert[u / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ||
        ( ( pBNS->vert[u / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) ) &&
          ( ( w_is_taut = ( pBNS->vert[w / 2 - 1].type & pBNS->type_T ) == pBNS->type_T ) ||
         ( ( pBNS->vert[w / 2 - 1].type & pBNS->type_CN ) == pBNS->type_CN ) ) &&
         u_is_taut + w_is_taut == 1)
    {
        /* rescap must have already been checked */
        return 1;
    }
#endif

    return 0;
}
#endif /* } FIX_TACN_POSSIBLE_BUG { */



#if ( FIX_KEEP_H_ON_NH_ANION == 1 )


/****************************************************************************
bIsRemovedHfromNHaion( ... )

Detect an attempt to remove H from -NH(-) to make =N(-);
all taut atoma except N are 'acidic'
****************************************************************************/
int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v )
{
    int    i, u2, v2, vat2;
    Vertex vtg, vat;
    BNS_VERTEX *pvAT, *pvCN;
    BNS_EDGE   *pEdge;
    if (!pBNS->type_TACN || u <= 1 || v <= 1 ||
         u % 2 || !( v % 2 ) /* the edge flow may only increase */)
    {
        return 0;
    }
    if (( pBNS->vert[u2 = u / 2 - 1].type & pBNS->type_TACN ) ||
        ( pBNS->vert[v2 = v / 2 - 1].type & pBNS->type_TACN ))
    {
        return 0; /* add/remove H is allowed for acidic atoms */
    }
    if (!pBNS->type_T || !pBNS->type_CN)
    {
        return 0; /* should not happen */
    }
    /* find which of u, v vertices is N and which is t-group */
    if (( ( pBNS->vert[u2].type & pBNS->type_T ) == pBNS->type_T ) && v2 < pBNS->num_atoms)
    {
        vtg = u;
        vat = v;
    }
    else
    {
        if (( ( pBNS->vert[v2].type & pBNS->type_T ) == pBNS->type_T ) && u2 < pBNS->num_atoms)
        {
            vtg = v;
            vat = u;
        }
        else
        {
            return 0;
        }
    }

    vat2 = vat / 2 - 1;
    pvAT = pBNS->vert + vat2;  /* atom */
    for (i = pvAT->num_adj_edges - 1; 0 <= i; i--)
    {
        pEdge = pBNS->edge + pvAT->iedge[i];
        pvCN = pBNS->vert + ( pEdge->neighbor12 ^ vat2 );
        if (( ( pvCN->type & pBNS->type_CN ) == pBNS->type_CN ) && pEdge->flow > 0)
        {
            return 1; /* detected */
        }
    }

    return 0;
}
#endif


#if ( FIX_AVOID_ADP == 1 )

/****************************************************************************
bIsAggressiveDeprotonation( ... )

Detect  (tg)-N=A-A=A-A=N-(tg)
u v  w
k =    5   4 3 2 1 0 1  2
^
odd number means ADP
****************************************************************************/
int bIsAggressiveDeprotonation( BN_STRUCT* pBNS,
                                Vertex v,
                                Vertex w,
                                Edge *SwitchEdge )
{
#define TYPE_T   1   /* t-group [also called H-group] */
#define TYPE_CN  2   /* (-)c-group */
#define TYPE_AT  4
    int    k, v2, u2, w2, u2_next, type0, type1, type2, type;
    Vertex u, u_next;
    EdgeIndex iuv;

    if (v <= 1 || w <= 1)
    {
        return 0;
    }

    if (!pBNS->type_TACN || !pBNS->type_T || !pBNS->type_CN)
    {
        return 0; /* should not happen */
    }

    v2 = v / 2 - 1;
    w2 = w / 2 - 1;
    if (v2 >= pBNS->num_atoms || w2 < pBNS->num_atoms)
    {
        goto cross_edge;
    }

    if (!( ( pBNS->vert[w2].type & pBNS->type_T ) == pBNS->type_T ) &&
         !( ( pBNS->vert[w2].type & pBNS->type_CN ) == pBNS->type_CN ))
    {
        goto cross_edge;
    }

    /* v ia an atom, w is a t-group, v != w' */
    for (k = 0, u = v; 1 < ( u_next = u, u = GetPrevVertex( pBNS, u, SwitchEdge, &iuv ) ); k++)
    {
        u2 = u / 2 - 1;
        if (u2 >= pBNS->num_atoms)
        {
            /* Moving backward along the alt path we have found a vertex
            that is not an atom. Possibly it is a t- or (-)c-group */
            if (!( k % 2 ))
            {
                return 0; /* even vertex -- always okay */
            }
            if (!( ( pBNS->vert[u2].type & pBNS->type_T ) == pBNS->type_T ) &&
                 !( ( pBNS->vert[u2].type & pBNS->type_CN ) == pBNS->type_CN ))
            {
                /* not a t- or (-)c-group */
                return 0;
            }
            u2_next = u_next / 2 - 1;
            if (!( pBNS->vert[v2].type & pBNS->type_TACN ) &&
                 !( pBNS->vert[u2_next].type & pBNS->type_TACN ))
            {
                /* none of the atoms at the ends are N */
                return 0;
            }
            return 1;
        }
    }

    return 0;

cross_edge:

    /*****************************************************************************
    * v and w (v=w') are same vertex reached with opposite "phases".
    * w cannot be (t) because this would have been detected earlier -- ???
    *   (t)-A=A-A=A-A=A-(t)
    *           v
    *    3  2 1 0 1 2 3  4
    *   kv               kw
    *   (kv + kw)%2 == 1  <==> aggressive deprotonation
    *****************************************************************************/

    if (v == prim( w ))
    {
        type0 = 0;
        if (v2 >= pBNS->num_atoms)
        {
            type0 = ( ( pBNS->vert[v2].type & pBNS->type_T ) == pBNS->type_T ) ? TYPE_T :
                ( ( pBNS->vert[v2].type & pBNS->type_CN ) == pBNS->type_CN ) ? TYPE_CN : 0;
        }
    }

    return 0;
}
#endif


/****************************************************************************/
int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv )
{
    BNS_ST_EDGE    *pst_edge;
    BNS_EDGE       *pedge;

    int f;
    S_CHAR s_or_t;
    int    bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t );

    if (!IS_BNS_ERROR( bBackward ))
    {

        if (s_or_t)
        {
            pst_edge = (BNS_ST_EDGE *) pedge;
            f = ( pst_edge->flow & EDGE_FLOW_ST_MASK );
            if (!bBackward)
            {
                f = (int) pst_edge->cap - f;
            }
        }
        else
        {
            f = ( pedge->flow & EDGE_FLOW_MASK );
            if (!bBackward)
            {
                f = (int) pedge->cap - f;
            }
        }
        return f;
    }

    return bBackward; /* error */
}


/****************************************************************************
W.Kocay, D.Stone,
"An Algorithm for Balanced Flows",
The Journal of Combinatorial Mathematics and Combinatorial Computing,
vol. 19 (1995) pp. 3-31

W.Kocay, D.Stone,
"Balanced network flows",
Bulletin of the Institute of Combinatorics and its Applications,
vol. 7 (1993), pp. 17--32

N = a balanced network (bipartite directed graph) of:
n=2*V+2 vertices (incl. s (source) and t (target);
each other vertex i is included 2 times:
set X (x[i]) of V vertices (v) and a set Y (y[j]) of
V complementary vertices (v') so that x[i]' = y[i], x[i]''=x[i], and
m=2*E+2*V edges (each original undirected edge i-j is represented as
2 directed edges: x[i]->y[j] and x[j]->y[i]; plus
V edges s->x[i] and V edges y[j]->t)
v'   = a complement vertex to v
v'u' = (uv)' = a complement edge to uv
rescap(uv) = cap(uv)-f(uv) if uv is a forward edge
= f(uv)         if uv is a backward edge
(i)   0 <= f(uv) <= cap(uv)
(ii)  f+(u) = f-(u) for all u in X, Y where f+(u) is a total flow out of u,
and f-(u) is a total flow into u
(iii) f(uv) = f((uv)') (balanced flow condition)

S     = a set of all s-searchable vertices
S-    = all other vertices
if t in S, then N contains a valid augmenting path P, so that flow can be
augmented on both P and P'
if t not in S, then let K=[S,S-], the set of all edges directed from S to S-.
K is an edge-cut that has a special structure.
Let
A  = {x[i], y[i] |x[i] not in S, y[i]     in S}
B  = {x[i], y[i] |x[i]     in S, y[i] not in S}
C  = {x[i], y[i] |x[i]     in S, y[i]     in S}
D  = {x[i], y[i] |x[i] not in S, y[i] not in S}
N[C]  = subgraph of N induced by C;
it consists of of a number of connected components C[i]
K[i]  = those edges of K with one endpoint in C[i].

If t is in S- then

i)   Each C[i] = C[i]'
ii)  There are no edges between C and D
iii) Each K[i] has odd capacity, it is called a balanced edge-cut.

balcap(K) = cap(K) - odd(K), where odd(K) is the number of connected components in N[C].
Name "odd(K)" is because each cap(K[i]) is odd.

Max-Balanced-Flow-Min-Balanced-Cut Theorem:

Let f be a balanced flow in N, and let K be any balanced edge-cut.
The value of a maximum balanced flow equals the capacity of a minimum
edge-cut, that is, val(f) = balcap(K) when f is maximum and K is minimum.

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


/*******************************************************/
/*                                                     */
/*        VERTEX NUMBERING                             */
/*                                                     */
/*   Total number of atoms    = n                      */
/*   Total number of vertices = 2*n+2                  */
/*******************************************************/
/*                                                     */
/* atom numbering starts from 0:                       */
/*                                                     */
/* atoms    s t x0 y0 x1 y1 ... xi   yi   ...xn   yn   */
/* vertices 0 1 2  3  4  5  ... 2i-2 2i-1 ...2n-2 2n-1 */
/*                                                     */
/* atom = vertex/2-1; if negative then s or t          */
/*                                                     */
/* vertex = (atom + 1) * 2 + i; i=0 for x, i=1 for y   */
/*                                                     */
/*******************************************************/

/*********************************************************************************/
/* v' variable is called prim(v) for now                                         */
/*********************************************************************************/

/*
BalancedNetworkSearch( ... )

N has source s, target t, the mirror network M is constructed.
The tree T contains a valid sv-path for each v in T. Simultaneously, the complementary
tree T' is built as indicated in comments. The trees T and T' must have no edges or
vertices in common. Initially T will be built as in breadth-first-search, and T' will
be the complementary tree, it will contain the complementary valid v't-path.

NB: this procedure is _not_ recursive

*/


/****************************************************************************/
int BalancedNetworkSearch( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )

{
    Vertex          *BasePtr = pBD->BasePtr;
    Edge            *SwitchEdge = pBD->SwitchEdge;
    S_CHAR          *Tree = pBD->Tree;
    Vertex          *ScanQ = pBD->ScanQ;
    int              QSize = pBD->QSize;
    Vertex          *Pu = pBD->Pu;
    Vertex          *Pv = pBD->Pv;
    int              max_len_Pu_Pv = pBD->max_len_Pu_Pv;

    /* added to translate into C */
    int i, k, degree, delta, ret = 0;
    Vertex u, b_u, v, b_v, w;
    EdgeIndex iuv;
#if ( BNS_RAD_SEARCH == 1 )
    int              n, bRadSearch = ( BNS_EF_RAD_SRCH & bChangeFlow ) && pBD->RadEndpoints;
    BRS_MODE         bRadSrchMode = RAD_SRCH_NORM;
    int              bRadSearchPrelim = 0;
    if (bRadSearch)
    {
        pBD->nNumRadEndpoints = 0;
        bRadSrchMode = pBD->bRadSrchMode;
        bRadSearchPrelim = pBNS->type_TACN && bRadSrchMode == RAD_SRCH_NORM;
    }
#endif

    /*  -- Always --
    Vertex_s = FIRST_INDX;
    Vertex_t = Vertex_s+1;
    */
    QSize = k = 0;     /* put s on ScanQ = set S */
    ScanQ[QSize] = Vertex_s;
    BasePtr[Vertex_t] = Vertex_s;
    BasePtr[Vertex_s] = BLOSSOM_BASE; /* create initial blossom C(Vertex_s) with base s */
    Tree[Vertex_s] = TREE_IN_1;

    do
    {
        u = ScanQ[k]; /* select u from the head of ScanQ */
                      /* since u is on the queue, it has a blossom C(U) with base b_u */
        b_u = FindBase( u, BasePtr );
        degree = GetVertexDegree( pBNS, u );
#if ( BNS_RAD_SEARCH == 1 )
        n = 0;
#endif
        for (i = 0; i < degree; i++)
        {
            /* v = vert[u].neighbor[i]; */
            v = GetVertexNeighbor( pBNS, u, i, &iuv );
            if (v == NO_VERTEX)
            {
                continue; /* the atom has only single bonds, ignore it */
            }
#if ( BNS_RAD_SEARCH == 1 )
            if (!k && bRadSrchMode == RAD_SRCH_FROM_FICT && v / 2 <= pBNS->num_atoms)
            {
                continue; /* start from fict. vertices only */
            }
            if (bRadSearchPrelim && v / 2 > pBNS->num_atoms)
            {
                continue; /* during initial add/remove H allow radical movement only through real atoms */
            }
#endif
            if ( /* PrevPt[u] != v ** avoid edges of T */
                ( SwitchEdge_Vert1( u ) != v || SwitchEdge_Vert2( u ) != u )  /* avoid edges of T */
                 && ( ret = rescap( pBNS, u, v, iuv ) ) > 0)
            {
                /* Special treatment to prevent H<->(-) replacement on non-acidic atoms */
                /*----------------------------------------------------------------------*/
                if (pBNS->type_TACN)
                {
                    if (bIgnoreVertexNonTACN_atom( pBNS, u, v ))
                    {
                        continue;
                    }
                    if (bIgnoreVertexNonTACN_group( pBNS, u, v, SwitchEdge ))
                    {
                        continue;
                    }
#if ( FIX_KEEP_H_ON_NH_ANION == 1 )
                    if (bIsRemovedHfromNHaion( pBNS, u, v ))
                    {
                        continue;
                    }
#endif
#if ( FIX_AVOID_ADP == 1 )
                    if (bIsAggressiveDeprotonation( pBNS, u, v, SwitchEdge ))
                    {
                        continue;
                    }
#endif
                }
                /*----------------------------------------------------------------------*/
                b_v = FindBase( v, BasePtr ); /* Notation: b_x is a base of x */

                if (b_v == NO_VERTEX)
                {
                    /* Originally 0 instead of NO_VERTEX */

                    /* Important note: following "A. Note of Implementing the Algorithm" from the
                    article by Kocay and Stone, all references to PrevPt[a] have been
                    replaced with SwitchEdge[a][0]=SwitchEdge_Vert1(a); to be on a safe side
                    the check whether (SwitchEdge_Vert2(a)==a) has been added.
                    */

                    /* v is not yet in T or T' -- add it to T and M */
                    /*PrevPt[v] = u; ** this effectively adds v to T and v' to T' */

                    QSize++;
                    ScanQ[QSize] = v;
                    TREE_MARK( v, TREE_IN_1 ); /* mark v s-reachable (in T) */
                    TREE_MARK( prim( v ), TREE_IN_2 ); /* mark v' in T' */

                                                       /* Switch Edges: If u in T then Pu (a valid su-path) can be constructed
                                                       by successfully executing u=PrevPt[u] until u=s.
                                                       For vertices in T' the situation is different.
                                                       Suppose uv and v'u' are added to a mirror network M creating a blossom:

                                                       s            T    (Note: in the code v' is prim(v),
                                                       T             / \                       u' is prim(u), etc.)
                                                       /   ...
                                                       w=x1
                                                       / \          u in T,   v in T'
                                                       u=y2   v'=y3
                                                       \ /      <--- two added edges uv and (uv)'=v'u'
                                                       --------    X   ------------intersection of the edges ------------------------
                                                       / \      <--- (the edges intersection in the picture is shown as X)
                                                       u'=x2   v=x3      u' it T', v' in T
                                                       \ /
                                                       T'           w'=y1
                                                       \   ...
                                                       \ /
                                                       t           T'

                                                       Vertices v and u' now become s-reachable;
                                                       The valid paths to v and u' must use the edges uv and v'u' respectively.

                                                       For each vertex z in S we define a switch-edge that allows a valid sz-path
                                                       to be constructed, SwitchEdge[v]=uv and SwitchEdge[u']=v'u'. (We don't
                                                       know the direction of the edge uv, it may be (u,v) or (v,u). In either case,
                                                       the complementary edge is indicated by v'u').

                                                       Vertex w' also becomes s-reachable when uv is added to M, and a valid sw'-path
                                                       must use one of uv and v'u'. Therefore we choose one of them, say uv (see below
                                                       the rule of choosing the switch-edge), and define SwitchEdge[w'] = uv.

                                                       When the addition of an edge uv to M causes a vertex z to become s-reachable
                                                       (where z was previously non-reachable), z is placed on the ScanQ, that is, into S.
                                                       The edge uv is said to be a switch-edge for z.

                                                       Rule: We choose the the order of the vertices uv to be such that the valid sz-path
                                                       consists of a valid su-path, followed by edge uv, followed by a valid vz-path.

                                                       For vertices z in T we can take SwitchEdge[z]=yz where y=PrevPt[z] since
                                                       it is the edge yz that allows z to be s-reachable.
                                                       For vertices z not in S we take SwitchEdge[z]=NONE.

                                                       */

                                                       /* v is not yet in T or T' -- add it to T and M */
                    SwitchEdge_Vert1( v ) = u; /* this effectively adds uv and v'u' to M */
                    SwitchEdge_IEdge( v ) = iuv;

                    BasePtr[prim( v )] = v;
                    BasePtr[v] = BLOSSOM_BASE; /* create a trivial blossom C(v) with base v */
#if ( BNS_RAD_SEARCH == 1 )
                    n++;
#endif
                }

                else if (TREE_IS_S_REACHABLE( prim( v ) ) /*Is_s_Reachable(prim(v)*/
                                                          /* if v' is reachable, an st-path is given by P(u)-uv-P'(v') */
                                                          /*&& PrevPt[prim(u)] != prim(v) ** avoid edges of T' */
                          && ( SwitchEdge_Vert1( prim( u ) ) != prim( v ) || SwitchEdge_Vert2( prim( u ) ) != prim( u ) ) /* avoid edges of T' */
                          && b_u != b_v
                          && !( pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim( v ), u, SwitchEdge ) )
#if ( FIX_KEEP_H_ON_NH_ANION == 1 )
                          && !( pBNS->type_TACN && bIsRemovedHfromNHaion( pBNS, prim( v ), u ) )
#endif
                          )
                {
#if ( BNS_RAD_SEARCH == 1 )
                    n++;
#endif
                    /* There is now a valid sv-path via u avoiding b_v (unless v==b_v)
                    => u, v, u', and v' now all become part of the same connected component of M[C] */

                    /* djb-rwth: addressing coverity ID #499578 -- negative values of b_u handled properly */
                    w = MakeBlossom( pBNS, ScanQ, &QSize, Pu, Pv, max_len_Pu_Pv, SwitchEdge, BasePtr, u, v, iuv, b_u, b_v, Tree );
                    /* this constructed the new blossom and returned its base */

                    if (IS_BNS_ERROR( w ))
                    {
                        pBD->QSize = QSize;
                        return w; /* error */
                    }

                    b_u = w; /* the new base of C(u) */
                    if (prim( w ) == Vertex_t)
                    {
                        /* t is now s-reachable, a valid augmenting path P exists in M */
                        delta = FindPathCap( pBNS, SwitchEdge, Vertex_s, Vertex_t, 10000 ); /* compute the residual capacity of P + P' */
                        if (IS_BNS_ERROR( delta ))
                        {
                            pBD->QSize = QSize;
                            return delta; /* error */
                        }
#if ( ALLOW_ONLY_SIMPLE_ALT_PATH == 1 )
                        if (pBNS->bNotASimplePath || abs( delta ) > 1)
                        {
                            delta = 0;
                        }
#endif
                        if (delta)
                        {
                            pBNS->bChangeFlow |= ( bChangeFlow & BNS_EF_CHNG_FLOW );
                        }
                        ret = PullFlow( pBNS, SwitchEdge, Vertex_s, Vertex_t, delta, 0, bChangeFlow ); /* augment on a pair of valid st-paths */
                        pBD->QSize = QSize;
                        return
                            ( IS_BNS_ERROR( ret ) ? ret : delta );
                    }
                }
            }

            else if (IS_BNS_ERROR( ret ))
            {
                pBD->QSize = QSize;
                return ret; /* error */
            }
        }

#if ( BNS_RAD_SEARCH == 1 )
        if (bRadSearch && !n)
        {
            /* The BNS stopped at u */
            n = RegisterRadEndpoint( pBNS, pBD, u );
            if (IS_BNS_ERROR( n ))
            {
                pBD->QSize = QSize;
                return n;
            }
        }
#endif

        k++; /* advance ScanQ */
    } while (k <= QSize);


    /* if this point is reached, no valid augmenting path exists, ScanQ contains
    the set S of all s-reachable vertices and K=[S,S-] is a minimum balanced edge-cut */
    /* ClearFlowMarks( vert, num_vert); */

    pBD->QSize = QSize;

    return 0;
}


/*
Blossoms.

The vertices of a mirror network M consist T U T'. Intersection T ^ T' is empty.

The edges of M consist of switch-edges and their complements because edges
are added in complementary pairs, one of which is always a switch-edge.

The base of every blossom is in T.
Let C(i) be a blossom with base b_i. Since C(i)=C(i)', C(i) contains vertices of T and T'.
Since every valid sv-path to v in C(i) contains b_i, b_i is the first s-reachable vertex of C(i).

Suppose the mirror network M contains a valid sz-path P(z) to all vertices z in ScanQ.
Every vertex of P(z) is s-reachable therefore its vertices are all in blossoms or
trivial blossoms.

Let z be an s-reachable vertex and P(z) be a valid path in M.
Then every valid sz-path in M contains exactly the same sequence of blossom bases as P(z).

*/



/***********************************************************************
BasePtr[u] = -2  NO_VERTEX       u is not a blossom
-1  BLOSSOM_BASE    u is the base of its blossom
v                  a vertex closer to the base
************************************************************************/
Vertex FindBase( Vertex u, Vertex *BasePtr )
{
    if (BasePtr[u] == NO_VERTEX)
    {
        return NO_VERTEX;
    }
    else if (BasePtr[u] == BLOSSOM_BASE)
    {
        return u;
    }
    else
    {
        Vertex b;
        b = FindBase( BasePtr[u], BasePtr );
        BasePtr[u] = b; /* path compression */
        return b;
    }

}


/****************************************************************************
Returns index of the last path element and the path
****************************************************************************/
int FindPathToVertex_s( Vertex x,
                        Edge *SwitchEdge,
                        Vertex *BasePtr,
                        Vertex *Path,
                        int MaxPathLen )
{
    /* x is the base of a blossom, construct a valid Path of blossom bases to s */
    int i = 0;
    Path[i] = x;
    while (x != Vertex_s)
    {
        x = FindBase( SwitchEdge_Vert1( x ), BasePtr );
        if (++i < MaxPathLen)
        {
            Path[i] = x;
        }
        else
        {
            return BNS_WRONG_PARMS;
        }
    }

    return i;
}



/****************************************************************************
Make a blossom
****************************************************************************/
Vertex MakeBlossom( BN_STRUCT* pBNS,
                    Vertex *ScanQ,
                    int *pQSize,
                    Vertex *Pu,
                    Vertex *Pv,
                    int max_len_Pu_Pv,
                    Edge *SwitchEdge,
                    Vertex *BasePtr,
                    Vertex u,
                    Vertex v,
                    EdgeIndex iuv,
                    Vertex b_u,
                    Vertex b_v,
                    S_CHAR *Tree )
{
    /* In order to find the base of the new blossom, the paths
    P(u) and P(v') are constructed and compared in order to
    find the last blossom base they have in common which
    is reachable on a valid path.

    Edge uv connects two blossoms, their bases are b_u and b_v.
    */
    Vertex w, z;
    int len_Pu, len_Pv;
    int i, j, k;
    EdgeIndex izw;

    len_Pu = FindPathToVertex_s( b_u, SwitchEdge, BasePtr, Pu, max_len_Pu_Pv );
    if (IS_BNS_ERROR( len_Pu ))
    {
        return len_Pu;
    }
    len_Pv = FindPathToVertex_s( b_v, SwitchEdge, BasePtr, Pv, max_len_Pu_Pv );
    if (IS_BNS_ERROR( len_Pv ))
    {
        return len_Pv;
    }
    i = len_Pu;
    j = len_Pv;
    /* Initially Pu[i] and Pv[j] both equal to s, but their first elements are different */
    /* Find the last blossom base common to Pu and Pv */
    while (i >= 0 && j >= 0 && Pu[i] == Pv[j])
    {
        /* Was (Pu[i]==Pv[j] && i>=0 && j>=0) => tested Pu[-1], Pv[-1] <- pointed by W.Ihlenfeldt 08-26-2004*/
        i--;
        j--;
    }
    i++;
    w = Pu[i]; /* w is the last common vertex */
    z = SwitchEdge_Vert1( w );
    izw = SwitchEdge_IEdge( w );
    /* now extend the blossom if rescap(zw) >= 2 */
    while (w != Vertex_s && rescap( pBNS, z, w, izw ) >= 2)
    {
        i++;
        w = Pu[i];
        z = SwitchEdge_Vert1( w );
        izw = SwitchEdge_IEdge( w );
    }
    /* w is the base of the new blossom */
    /* first follow the path Pu from w to b_u */
    for (k = i - 1; k >= 0; k--)
    {
        z = Pu[k];  /* z is the base of the blossom */
        BasePtr[z] = w;
        BasePtr[prim( z )] = w; /* w is the new base of the blossom */
                                /* z and z' may already be part of a blossom that is being
                                swallowed into a larger blossom.
                                We don't want to change the switch edge in that case.
                                */

        if (!TREE_IS_ON_SCANQ( prim( z ) ) /*!IsInScanQ(prim(z)) */)
        {
            SwitchEdge_Vert1( prim( z ) ) = prim( v );     /* set the switch edge of z' */
                                                           /* SwitchEdge[prim(z)][1] = prim(u);  */
            SwitchEdge_IEdge( prim( z ) ) = iuv;
            ( *pQSize )++;
            ScanQ[*pQSize] = prim( z );    /* add z' to ScanQ */
            TREE_MARK( prim( z ), TREE_IN_2BLOSS ); /* mark z' s-reachable */
        }
    }
    /* Now follow the path Pv */
    for (k = j; k >= 0; k--) /* djb-rwth: converting for loop into while loop to avoid LLVM warning */
    {
        z = Pv[k]; /* z is the base of the blossom */
        BasePtr[z] = w;
        BasePtr[prim( z )] = w; /* w is the new base of the blossom */
                                /* z and z' may already be part of a blossom that is being
                                swallowed into a larger blossom.
                                We don't want to change the switch edge in that case.
                                */

        if (!TREE_IS_ON_SCANQ( prim( z ) ) /*!IsInScanQ(prim(z)) */)
        {
            SwitchEdge_Vert1( prim( z ) ) = u;     /* set the switch edge of z' */
                                                   /* SwitchEdge[prim(z)][1] = v;  */
            SwitchEdge_IEdge( prim( z ) ) = iuv;
            ( *pQSize )++;
            ScanQ[*pQSize] = prim( z );    /* add z' to ScanQ */
            TREE_MARK( prim( z ), TREE_IN_2BLOSS ); /* mark z' s-reachable */
        }
    }

    if (!TREE_IS_ON_SCANQ( prim( w ) ) /* !IsInScanQ(prim(w))*/)
    {   /* add w' to the blossom */
        SwitchEdge_Vert1( prim( w ) ) = u;     /* set the switch edge of w' */
                                               /* SwitchEdge[prim(w)][1] = v;  */
        SwitchEdge_IEdge( prim( w ) ) = iuv;
        ( *pQSize )++;
        ScanQ[*pQSize] = prim( w ); /* add w' to ScanQ */
        TREE_MARK( prim( w ), TREE_IN_2BLOSS );  /* mark w' s-reachable */
    }

    return w;
}


/****************************************************************************
When t is found to be s-reachable, a valid st-path P is known to exist.
Its complementary path P' is also valid. Once the residual capacity
delta(P) is known, the flow is augmented by calling PullFlow(s,t,delta).
It constructs the path P by using the switch-edges.
Let uv=SwitchEdge[t].
Then P is given by a valid su-path, followed by the edge uv, followed by
a valid vt-path.
PullFlow is a recursive procedure that constructs the path and its complement.

Let wz=SwitchEdge[y]. PullFlow(x, y, delta) uses the xw- and zy-portions of P
(see below). Since it must also augment on P' simultaneously, the zy-portion
is replaced by the y'z'-portion.

x                  y'
|                  |            P:   x--w--z--y
P |                  |  P'        P':  y'-z'-w'-x'
|                  o
o                   \
/   w'          z     \
/   o----\   /----o    /
\  /      \ /      \  /
\/        X        \/
/\       / \       /\
/  \ w   /   \  z' /  \
\   o----     ----o   /           Using a switch-edge wz and w'z'
\                   /            to construct P and P'
o                 o
|                 |
|                 |
x'                y


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



/*    PullFlow( ... )

Augment the flow by delta on all edges on a path P
between x and y in the order of the path;
AugmentEdge( pBNS, w, z, iwz, delta, 0 ) means the path is in w->z direction
AugmentEdge( pBNS, w, z, iwz, delta, 1 ) means the path is in w<-z direction

Unlike PullFlow in the paper by Kocay & Stone, here the augmentation
starts at "s", proceeds sequentially through the path end terminates at "t".
Since we do not really need the complement path, PullFlow ignores it.
*/


/****************************************************************************/
int PullFlow( BN_STRUCT *pBNS,
              Edge *SwitchEdge,
              Vertex x,
              Vertex y,
              int delta,
              S_CHAR bReverse,
              int bChangeFlow )
{
    Vertex w, z;
    EdgeIndex iwz;
    int ret = 0;

    w = SwitchEdge_Vert1( y );
    z = SwitchEdge_Vert2( y );
    iwz = SwitchEdge_IEdge( y );
    if (bReverse)
    {
        /* P consists of a path from x to w, then wz, then a path from z to y.  */
        /* z may equal y, in which case z is just PrevPt[y] */
        if (z != y)
        {
            ret = PullFlow( pBNS, SwitchEdge, prim( y ), prim( z ), delta, (S_CHAR) ( 1 - bReverse ), bChangeFlow ); /* augment between z and y */
        }
        if (!IS_BNS_ERROR( ret ))
        {
            ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow );
        }
        /* Do not augment the complementary path: AugmentEdge( prim(z), prim(w), vert, delta); */
        /* w may equal x, in which case there is no need to call PullFlow(x, w) */
        if (w != x && !IS_BNS_ERROR( ret ))
        {
            ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */
        }
    }
    else
    {
        /* P consists of a path from x to w, then wz, then a path from z to y.  */
        /* w may equal x, in which case there is no need to call PullFlow(x, w) */
        if (w != x && !IS_BNS_ERROR( ret ))
        {
            ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */
        }
        if (!IS_BNS_ERROR( ret ))
        {
            ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow );
        }
        /* z may equal y, in which case z is just PrevPt[y] */
        if (z != y && !IS_BNS_ERROR( ret ))
        {
            ret = PullFlow( pBNS, SwitchEdge, prim( y ), prim( z ), delta, (S_CHAR) ( 1 - bReverse ), bChangeFlow ); /* augment between z and y */
        }
    }

    return ret;
}


/****************************************************************************
Before augmenting on the two paths, it is necessary to find delta(P).
This can be done by following the paths and computing the minimum
residual capacity of all edges on P. An edge on both P and P' counts
for only half of its actual residual capacity, since augmentng on P by
delta will simutaneously reduce its capacity on P' by delta.
The path P can only be followed by using the switch-edges, as in PullFlow(...).
FindPathCap( x, y, delta ) is a recursive procedure that finds the residual
capacity on the portion of P between x and y. delta is the minimum capacity
found so far along the path.
****************************************************************************/


/****************************************************************************
Find the minimum residual capacity of all edges
between x and y in a valid st-path P.
delta is the minimum found so far
the vertices occur in order s,...,x,...,y,...,t along P
the vertices occur in order s,...,y',...,x',...,t along P'
****************************************************************************/
int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta )
{

    Vertex w, z, iwz;
    int    cap, delta2;

    /*    static int level;
    if ( level ++ > 50 )
    {
    #ifdef _DEBUG
    int stop = 1;
    #else
    ;
    #endif
    }
    */

    w = SwitchEdge_Vert1( y );
    z = SwitchEdge_Vert2( y ); /* wz is on the path P */
    iwz = SwitchEdge_IEdge( y ); /* edge index */

                                 /* Rescap_mark() detects edges passed 2 times and reduces rescap */
    cap = rescap_mark( pBNS, w, z, iwz );

    if (IS_BNS_ERROR( cap ))
    {
        /* level --; */
        return cap;
    }
    if (cap < delta)
    {
        delta = cap;
    }
    /* P consists of a path from x to w, then wz, then a path from z to y */
    if (w != x)
    {
        delta2 = FindPathCap( pBNS, SwitchEdge, x, w, delta );
        delta = inchi_min( delta2, delta );
    }
    if (z != y)
    {
        delta2 = FindPathCap( pBNS, SwitchEdge, prim( y ), prim( z ), delta );
        delta = inchi_min( delta2, delta );
    }
    /* level --; */

    return delta;
}


/*
BT = bond types
*/


#define BT_ALTERN_BOND           1      /* 1-2, possibly stereo */
#define BT_OTHER_ALTERN_BOND     2      /* 1-3, 2-3, 1-2-3 alternating non-stereo non-taut bonds */

#define BT_ALTERN_NS_BOND        4

#define BT_TAUTOM_BOND           8

#define BT_ALTERN_UNKN_BOND     16

#define BT_IGNORE_BOND           0

#define BT_NONSTEREO_MASK        (BT_TAUTOM_BOND|BT_ALTERN_NS_BOND)

#define BT_ALT_BOND_MASK         (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND)

#define BT_NONTAUT_BOND_MASK     (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND)

/* BNS members redefinitions for finding non-stereo bonds */
/* BNS_EDGE */
#define nBlockNumberAltBns   flow   /* internal variable of the DFS traversal: mark traversed bonds */
#define nNumAtInBlockAltBns  cap
#define nBondTypeInpAltBns   pass    /* 0=>cannot be stereo at all, 1=>alt or taut non-stereo, 2=>can be stereo */
#define nBondNonStereoAltBns cap     /* 1=>found to be non-stereogenic although BondTypeInp=2; 0=>as in BondTypeInp */

#if ( BNS_MARK_ONLY_BLOCKS == 1 )   /* { */
/* BNS_VERTEX */
#define bCutVertexAltBns         st_edge.cap0  /* cut-vertex flag */
#define nRingSystemAltBns        st_edge.cap   /* ordering number of a ring system */
#define nNumAtInRingSystemAltBns st_edge.flow0 /* number of vertices in a ring system */
#define nBlockSystemAltBns       st_edge.flow  /* result of the DFS traversal: even cirquit must be within one block */

#endif  /* } */

#define valenceAltBns            num_adj_edges


/****************************************************************************/
int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo )
{
    AT_NUMB   *nStackAtom = NULL;
    int        nTopStackAtom;
    AT_NUMB   *nRingStack = NULL;
    int        nTopRingStack; /* was AT_NUMB */
    AT_NUMB   *nBondStack = NULL;
    int        nTopBondStack;
    AT_NUMB   *nDfsNumber = NULL;
    AT_NUMB   *nLowNumber = NULL;
    S_CHAR    *cNeighNumb = NULL;
    AT_NUMB    nDfs;
    AT_NUMB    nNumAtInRingSystem;
    int        i, j, u, w, start, nNumRingSystems; /* djb-rwth: removing redundant variables */
    BNS_VERTEX *at = pBNS->vert;
    BNS_EDGE   *bond = pBNS->edge;
    int        num_atoms = pBNS->num_atoms;
    int        num_edges = pBNS->num_bonds;

    /* Allocate arrays */
    nStackAtom = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nStackAtom[0] ) );
    nRingStack = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nRingStack[0] ) );
    nDfsNumber = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nDfsNumber[0] ) );
    nLowNumber = (AT_NUMB *) inchi_malloc( num_atoms * sizeof( nLowNumber[0] ) );
    nBondStack = num_edges ? ( (AT_NUMB *) inchi_malloc( num_edges * sizeof( nBondStack[0] ) ) ) : (AT_NUMB *) ( NULL ); /* special case: no bonds 2006-03 */
    cNeighNumb = (S_CHAR  *) inchi_malloc( num_atoms * sizeof( cNeighNumb[0] ) );

    /* Check allocation */
    if (!nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || (!nBondStack && num_edges) || !cNeighNumb
         ) /* djb-rwth: addressing LLVM warning */
    {
        nNumRingSystems = CT_OUT_OF_RAM;  /*  program error */ /*   <BRKPT> */
        goto exit_function;
    }

    /********************************************
    *
    * Find Cut-vertices & Blocks
    *
    *             1\      /5   has 3 blocks (maximal subgraphs that
    *  Example:   | >3--4< |   are nonseparable by deleting a single vertex):
    *             2/      \6   (1,2,3, has 3 bonds), (3,4, has 1 bond), and (4,5,6, has 3 bonds)
    *
    *                          Cut-vertices or articulation points are
    *                          intersections of the blocks: points 3 and 4.
    ********************************************/

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

    RingSystemAlt are atoms connected by alternating bonds
    (as must be indicated in bIsAltBond()):

    BOND_ALTERN
    BOND_ALT_123
    BOND_ALT_13
    BOND_ALT_23

    Since other bonds may be present, we possibly need
    to restart to move to another component
    *********************************************************/

    nNumRingSystems = 0;
    memset( nDfsNumber, 0, num_atoms * sizeof( nDfsNumber[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */

    for (start = 0; start < num_atoms; start++)
    {
        if (nDfsNumber[start])
        {
            continue;
        }
        for (i = 0; i < at[start].valenceAltBns; i++)
        {
            if (bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALTERN_BOND)
                goto found_alt;
        }
        continue;

    found_alt:

        /*  Initiation */
        u = start; /*  start atom */
        nDfs = 0;
        nTopStackAtom = -1;
        nTopRingStack = -1;
        nTopBondStack = -1;
        memset( cNeighNumb, 0, num_atoms * sizeof( cNeighNumb[0] ) ); /* djb-rwth: memset_s C11/Annex K variant? */
        /*  Push the start atom on the stack */
        nLowNumber[u] = nDfsNumber[u] = ++nDfs;
        nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
        nRingStack[++nTopRingStack] = (AT_NUMB) u;

        /* djb-rwth: removing redundant code */

        do
        {
            /* advance */
            /*while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/
            /* replaced due to missing sequence point */
            while (i = (int) nStackAtom[nTopStackAtom], j = (int) cNeighNumb[i], (int) at[i].valenceAltBns > j)
            {
                cNeighNumb[i] ++;
                if (!( bond[w = at[i].iedge[j]].nBondTypeInpAltBns & BT_ALT_BOND_MASK ))
                {
                    continue;
                }
                u = (int) ( bond[at[i].iedge[j]].neighbor12 ^ i );
                if (!nDfsNumber[u] && nBondStack) /* djb-rwth: fixing a NULL pointer dereference */
                {
                    /* tree edge, 1st visit -- advance */
                    nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
                    nRingStack[++nTopRingStack] = (AT_NUMB) u;
                    nBondStack[++nTopBondStack] = (AT_NUMB) w;
                    nLowNumber[u] = nDfsNumber[u] = ++nDfs;
                    /* djb-rwth: removing redundant code */
                }
                else
                {
                    if (!nTopStackAtom || u != (int) nStackAtom[nTopStackAtom - 1])
                    { /*  may comment out ? */
                      /* back edge: u is not a predecessor of i */
                        if ((nDfsNumber[u] < nDfsNumber[i]) && nBondStack) /* djb-rwth: fixing a NULL pointer dereference */
                        {
                            /* Back edge, 1st visit: u is ancestor of i. Save and compare */
                            nBondStack[++nTopBondStack] = (AT_NUMB) w;
                            if (nLowNumber[i] > nDfsNumber[u])
                            {
                                nLowNumber[i] = nDfsNumber[u];
                            }
                        }
                    }
                }
            }
            cNeighNumb[i] = 0;

            /* Back up */
            if (i != start)
            {
                u = (int) nStackAtom[nTopStackAtom - 1]; /* predecessor of i */
                if (nLowNumber[i] >= nDfsNumber[u])
                {
                    /* Output the block; the block was entered through its first bond u->i */
                    nNumRingSystems++;
                    /*at[u].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */
                    nNumAtInRingSystem = 1;
                    /*
                    if ( u != start || nNumStartChildren > 1 ) {
                    at[u].bCutVertexAltBns += 1;  // mark cut-vertex (articulation point)
                    }
                    */
                    while (nTopRingStack >= 0)
                    {
                        j = nRingStack[nTopRingStack--];
                        /*at[j].nBlockSystemAltBns = nNumRingSystems;*/ /*  mark the atom */
                        nNumAtInRingSystem++;
                        if (i == j)
                        {
                            break;
                        }
                    }
                    while (nTopBondStack >= 0)
                    {
                        w = nBondStack[nTopBondStack--];
                        bond[w].nBlockNumberAltBns = nNumRingSystems; /*  mark the bond */
                        bond[w].nNumAtInBlockAltBns = nNumAtInRingSystem;
                        if ((i == bond[w].neighbor1 && u == ( i ^ bond[w].neighbor12 )) ||
                             (u == bond[w].neighbor1 && i == ( u ^ bond[w].neighbor12 ))) /* djb-rwth: addressing LLVM warning */
                        {
                            break;
                        }
                    }
                }
                else
                {
                    if (nLowNumber[u] > nLowNumber[i])
                    {
                        /* inherit */
                        nLowNumber[u] = nLowNumber[i];
                    }
                }
            }
        } while (--nTopStackAtom >= 0);
    }

#if ( BNS_MARK_ONLY_BLOCKS != 1 )  /* { */

    /********************************************
    *
    * Find Ring Systems
    * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges.
    *
    ********************************************/

    /*  Initiation */
    nNumRingSystems = 0;
    for (start = 0; start < num_atoms; start++)
    {
        if (at[start].nRingSystemAltBns)
        {
            continue;
        }
        for (i = 0; i < at[start].valenceAltBns; i++)
        {
            if (bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALT_BOND_MASK)
            {
                goto found_alt2;
            }
        }
        continue;

    found_alt2:
        u = start; /*  start atom */
        nDfs = 0;
        nTopStackAtom = -1;
        nTopRingStack = -1;
        memset( nDfsNumber, 0, num_atoms * sizeof( nDfsNumber[0] ) );
        memset( cNeighNumb, 0, num_atoms * sizeof( cNeighNumb[0] ) );
        /*  push the start atom on the stack */
        nLowNumber[u] = nDfsNumber[u] = ++nDfs;
        nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
        nRingStack[++nTopRingStack] = (AT_NUMB) u;

        do
        {
            /* advance */
        advance_ring:
            /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/
            /* replaced due to missing sequence point */
            if (i = (int) nStackAtom[nTopStackAtom], j = (int) cNeighNumb[i], (int) at[i].valenceAltBns > j)
            {
                cNeighNumb[i] ++;
                if (!( bond[at[i].iedge[j]].nBondTypeInpAltBns & BT_ALTERN_BOND ))
                {
                    goto advance_ring;
                }
                u = (int) ( bond[at[i].iedge[j]].neighbor12 ^ i );
                if (!nDfsNumber[u])
                {
                    /* tree edge, 1st visit -- advance */
                    nStackAtom[++nTopStackAtom] = (AT_NUMB) u;
                    nRingStack[++nTopRingStack] = (AT_NUMB) u;
                    nLowNumber[u] = nDfsNumber[u] = ++nDfs;
                }
                else
                {
                    if (!nTopStackAtom || u != (int) nStackAtom[nTopStackAtom - 1])
                    {
                        /* back edge: u is not a predecessor of i */
                        if (nDfsNumber[u] < nDfsNumber[i])
                        {
                            /* Back edge, 1st visit: u is ancestor of i. Compare */
                            if (nLowNumber[i] > nDfsNumber[u])
                            {
                                nLowNumber[i] = nDfsNumber[u];
                            }
                        }
                    }
                }
                goto advance_ring;
            }
            else
            {
                cNeighNumb[i] = 0;
            }

            /* Back up */
            if (nDfsNumber[i] == nLowNumber[i])
            {
                /*  Found a ring system */
                nNumRingSystems++;

                /*  Unwind nRingStack[] down to i */

                /*  Count atoms in a ring system */
                for (nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j--)
                {
                    nNumAtInRingSystem++;
                    if (i == (int) nRingStack[j])
                    {
                        break;
                    }
                }
                while (nTopRingStack >= 0)
                {
                    j = (int) nRingStack[nTopRingStack--];
                    at[j].nRingSystemAltBns = (AT_NUMB) nNumRingSystems; /*  ring system id */
                    at[j].nNumAtInRingSystemAltBns = nNumAtInRingSystem;
                    if (i == j)
                    {
                        /*  Reached atom on the top of nStackAtom[] stack  */
                        break;
                    }
                }
            }
            else
            {
                if (nTopStackAtom > 0)
                {
                    j = (int) nStackAtom[nTopStackAtom - 1];
                    /* inherit nLowNumber */
                    if (nLowNumber[j] > nLowNumber[i])
                    {
                        nLowNumber[j] = nLowNumber[i];
                    }
                }
            }
        } while (--nTopStackAtom >= 0);
    }

#endif /* }  BNS_MARK_ONLY_BLOCKS != 1 */

exit_function:
    if (nStackAtom)
    {
        inchi_free( nStackAtom );
    }
    if (nRingStack)
    {
        inchi_free( nRingStack );
    }
    if (nDfsNumber)
    {
        inchi_free( nDfsNumber );
    }
    if (nLowNumber)
    {
        inchi_free( nLowNumber );
    }
    if (nBondStack)
    {
        inchi_free( nBondStack );
    }
    if (cNeighNumb)
    {
        inchi_free( cNeighNumb );
    }

    return nNumRingSystems;
}


/****************************************************************************/
int ReInitBnStructForAltBns( BN_STRUCT *pBNS,
                             inp_ATOM *at,
                             int num_atoms,
                             int bUnknAltAsNoStereo )
{
    Vertex v, v2;
    int ret, bond_type, num_to_test, j;
    BNS_EDGE   *pBond;
    BNS_VERTEX *pAtom;

    /* Strip all t-groups and c-groups */
    num_to_test = 0;
    if (bUnknAltAsNoStereo)
    {
        for (j = 0; j < pBNS->num_edges; j++)
        {
            pBNS->edge[j].pass = 0;
        }
    }

    ret = ReInitBnStruct( pBNS, at, num_atoms, 0 );

    if (ret || pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges)
    {
        ret = BNS_REINIT_ERR;
        goto exit_function;
    }

    /* Eliminate bonds and fix st-caps */
    for (v = 0; v < num_atoms; v++)
    {
        pAtom = pBNS->vert + v;
        for (j = 0; j < pAtom->valenceAltBns; j++)
        {
            pBond = pBNS->edge + pAtom->iedge[j];
            if (pBond->neighbor1 == v)
            {
                bond_type = ( at[v].bond_type[j] & BOND_TYPE_MASK );
                v2 = pBond->neighbor12 ^ v;
                if (at[v].endpoint || at[v2].endpoint)
                {
                    bond_type = 0; /* any bond to an endpoint considered non-stereogenic */
                }
#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 )
                if (bUnknAltAsNoStereo)
                {
                    if (bond_type == BOND_ALTERN && at[v].bond_stereo[j] == STEREO_DBLE_EITHER)
                    {
                        bond_type = 0; /* treat unknown (Either) ALT bond as non-stereo */
                    }
                }
#endif
                switch (bond_type)
                {

                    case BOND_ALTERN:
                        pBond->nBondTypeInpAltBns = BT_ALTERN_BOND;
                        num_to_test++;
                        break;

                    case BOND_ALT_123:
                    case BOND_ALT_13:
                    case BOND_ALT_23:
                        pBond->nBondTypeInpAltBns = BT_OTHER_ALTERN_BOND;
                        break;

                    case BOND_TAUTOM:
                        pBond->nBondTypeInpAltBns = BT_TAUTOM_BOND;
                        break;

                    case BOND_ALT12NS:
                        pBond->nBondTypeInpAltBns = BT_ALTERN_NS_BOND;
                        break;

                    case 0:
                    case BOND_SINGLE:
                    case BOND_DOUBLE:
                    case BOND_TRIPLE:
                        pBond->nBondTypeInpAltBns = BT_IGNORE_BOND;
                        break;

                    default:
                        pBond->nBondTypeInpAltBns = BT_IGNORE_BOND;
                        break;
                }
                pBond->nBondNonStereoAltBns = 0; /* djb-rwth: addressing GCC warning -- operations on pBond->cap might be undefined */
                pBond->nBlockNumberAltBns = 0;
                pBond->nNumAtInBlockAltBns = 0;

#if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
                pBond->forbidden &= pBNS->edge_forbidden_mask;
#endif
            }
        }
        pAtom->bCutVertexAltBns =
            pAtom->nRingSystemAltBns =
            pAtom->nNumAtInRingSystemAltBns =
            pAtom->nBlockSystemAltBns = 0;
    }

    return num_to_test;

exit_function:

    return ret;
}


/****************************************************************************/
int MarkNonStereoAltBns( BN_STRUCT *pBNS,
                         inp_ATOM *at,
                         int num_atoms,
                         int bUnknAltAsNoStereo )
{
    int       num_bonds = pBNS->num_bonds;
    int       ret;
    int       ibond, ib1, ib2;
    BNS_EDGE *pBond;
    Vertex    iat1, iat2;

    ret = 0;

    if (pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges)
    {
        ret = BNS_REINIT_ERR;
        goto exit_function;
    }
    if (bUnknAltAsNoStereo)
    {
        for (ibond = 0; ibond < num_bonds; ibond++)
        {
            pBond = pBNS->edge + ibond;
            if (pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND)
            {
                continue;
            }
            iat1 = pBond->neighbor1;
            iat2 = pBond->neighbor12 ^ iat1;
            ib1 = pBond->neigh_ord[0];
            ib2 = pBond->neigh_ord[1];
            if ( /* alt bond non-adjacent to a taut. endpoint: */
                ( pBond->nBondTypeInpAltBns == BT_ALTERN_BOND &&
                  pBond->nNumAtInBlockAltBns <= 3 )  /* non-ring bond */ ||
                 /* alt bond adjacent to a taut. endpoint: */
                  ( pBond->nBondTypeInpAltBns == BT_IGNORE_BOND &&
                 ( at[iat1].bond_type[ib1] & BOND_TYPE_MASK ) == BOND_ALTERN )
                 )
            {
                if (( at[iat1].bond_type[ib1] & BOND_TYPE_MASK ) == BOND_ALTERN)
                {
                    /* bond_type = BOND_ALT12NS; */
                    at[iat1].bond_stereo[ib1] =
                        at[iat2].bond_stereo[ib2] = STEREO_DBLE_EITHER;
                    ret++;
                }
            }
        }
    }
    else
    {
        for (ibond = 0; ibond < num_bonds; ibond++)
        {
            pBond = pBNS->edge + ibond;
            if (pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND)
            {
                continue;
            }
            iat1 = pBond->neighbor1;
            iat2 = pBond->neighbor12 ^ iat1;
            ib1 = pBond->neigh_ord[0];
            ib2 = pBond->neigh_ord[1];
            if ( /* alt bond non-adjacent to a taut. endpoint: */
                ( pBond->nBondTypeInpAltBns == BT_ALTERN_BOND &&
                  pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ ||
                 /* alt bond adjacent to a taut. endpoint: */
                  ( pBond->nBondTypeInpAltBns == BT_IGNORE_BOND &&
                 ( at[iat1].bond_type[ib1] & BOND_TYPE_MASK ) == BOND_ALTERN )
                 )
            {
                at[iat1].bond_type[ib1] =
                    at[iat2].bond_type[ib2] = BOND_ALT12NS;
                ret++;
            }
        }
    }

exit_function:

    return ret;
}


#if ( READ_INCHI_STRING == 1 )
/*****************************************************************************/
#ifndef RI_ERR_ALLOC
/* from ichirvrs.h */
#define RI_ERR_ALLOC   (-1)
#define RI_ERR_SYNTAX  (-2)
#define RI_ERR_PROGR   (-3)
#endif


/****************************************************************************
Check if atom bonded to charged atom
****************************************************************************/
int bHasChargedNeighbor( inp_ATOM *at, int iat )
{
    int i;
    for (i = 0; i < at[iat].valence; i++)
    {
        if (at[(int) at[iat].neighbor[i]].charge)
        {
            return 1;
        }
    }

    return 0;
}


/****************************************************************************
Add or remove protons

*num_protons_to_add = nToBeRemovedByNormFromRevrs

nToBeRemovedByNormFromRevrs > 0: less protons should be allowed to be
added by the Normalization of the Reconstructed Structure
nToBeRemovedByNormFromRevrs < 0: prepare more H(+) to be removed by
the InChI Normalization of the Reconstructed Structure

OrigStruct -> NormOrig + n(orig)*H(+)
RevrStruct -> NormRevr + n(revr)*H(+)
nToBeRemovedByNormFromRevrs = n(orig) - n(revr)  [each may be negative]

n(orig) > n(revr) or nToBeRemovedByNormFromRevrs > 0 means:
-----------------------------------------------------------
- Too many protons were added by the Normalization to the Reconstructed Structure
(a) n(revr) < 0 => protons were added while they should not have been added;
Solution: "neutralize" (-) charged proton acceptors by moving charges to other atoms
on the condition ADP cannot add in another way;
(b) n(orig) > n(revr) => 0  => too few protons were removed
Solution: (the easiest) attach H(+) to =O or -N< or -N=
Solution: move (+) from N or OH to an atom adjacent to (-) charge or to
an atom that is not N.

n(orig) < n(revr) or nToBeRemovedByNormFromRevrs < 0 means:
-----------------------------------------------------------
- Too few protons were added by the Normalization to the Reconstructed Stucture
(a) n(orig) < 0 => protons were not added while they should have been added;
Solution: move (-) to O by replacing =O with -O(-)
(b) 0 <= n(orig) < n(revr) => too many protons were removed

Note: it is critically important to takr into account cumbersome Normalization
Total Charge: if it is >= 0 then no H(+) may be removed from -OH or by ADP
However, if N(+) is present then ADP will always try to remove a proton
****************************************************************************/
int AddRemoveProtonsRestr( inp_ATOM *at,
                           int num_atoms,
                           int *num_protons_to_add,
                           int nNumProtAddedByRestr,
                           INCHI_MODE bNormalizationFlags,
                           int num_tg,
                           int nChargeRevrs,
                           int nChargeInChI )
{
    int i, j, ret = 0;
    int nAtTypeTotals[ATTOT_ARRAY_LEN];
    int   num_prot = *num_protons_to_add;
    int   type, mask, bSuccess, nTotCharge, nNumSuccess = 0;
    int max_j_Aa = -1, max_j_Ar = -1;

    /* for the reference:

    #define FLAG_NORM_CONSIDER_TAUT      ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \
    FLAG_PROTON_NP_HARD_REMOVED    | \
    FLAG_PROTON_AC_SIMPLE_ADDED    | \
    FLAG_PROTON_AC_SIMPLE_REMOVED  | \
    FLAG_PROTON_AC_HARD_REMOVED    | \
    FLAG_PROTON_AC_HARD_ADDED      | \
    FLAG_PROTON_SINGLE_REMOVED     | \
    FLAG_PROTON_CHARGE_CANCEL    )

    #define FLAG_FORCE_SALT_TAUT         ( FLAG_PROTON_NP_HARD_REMOVED  | \
    FLAG_PROTON_AC_HARD_REMOVED  | \
    FLAG_PROTON_AC_HARD_ADDED    )

    */

    /* if ChargeRevrs > nChargeInChI then we should prevent proton addition or facilitate proton removal
    a typical case is (=) on N or O instead of C(-)

    if ChargeRevrs < nChargeInChI then we should prevent proton removal or facilitate proton addition
    */

    mark_at_type( at, num_atoms, nAtTypeTotals );
    for (i = nTotCharge = 0; i < num_atoms; i++)
    {
        nTotCharge += at[i].charge;
    }
    /* Size for SimpleAddAcidicProtons() */
    for (max_j_Aa = 0; AaTypMask[2 * max_j_Aa]; max_j_Aa++)
    {
        ;
    }
    /* Size for SimpleRemoveAcidicProtons */
    for (max_j_Ar = 0; ArTypMask[2 * max_j_Ar]; max_j_Ar++)
    {
        ;
    }
    if (num_prot < 0 && nAtTypeTotals[ATTOT_TOT_CHARGE] - nNumProtAddedByRestr <= 0)
    {
        /* Remove proton(s) */
        /* use test from SimpleAddAcidicProtons() to test whether removal of H(+) from =C-OH, etc. is correct */
        for (i = 0; i < num_atoms && num_prot; i++)
        {
            /* Choose an atom */
            if (at[i].sb_parity[0] || at[i].p_parity || at[i].charge ||
                 !at[i].num_H || at[i].radical || bHasChargedNeighbor( at, i ))
            {
                continue;
            }
            /* try to remove a proton and check whether InChI would add it back */
            at[i].charge--;
            at[i].num_H--;
            type = GetAtomChargeType( at, i, NULL, &mask, 0 );
            at[i].charge++;
            at[i].num_H++;

            if (type)
            {
                for (bSuccess = 0, j = 0; j < max_j_Aa; j++)
                {
                    if ((bSuccess = ( type & AaTypMask[2 * j] ) && ( mask && AaTypMask[2 * j + 1] ))) /* djb-rwth: addressing LLVM warning */
                    {
                        break; /* the proton may be added to this atom */
                    }
                }
                if (bSuccess)
                {
                    /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                    type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
                    at[i].charge--;
                    at[i].num_H--;
                    type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
                    num_prot++; /* success */
                    nNumSuccess++;
                }
            }
        }
    }

    if (num_prot < 0 && num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE] - nNumProtAddedByRestr <= 0)
    {
        /* Alternative proton removal: O=C-NH => (-)O-C=N, O and N are taut. endpoints */
        int endp2, centp, k, i0, k0;
        for (i = 0; i < num_atoms; i++)
        {
            /* Choose an atom */
            if (!at[i].endpoint || at[i].sb_parity[0] || at[i].p_parity ||
                 at[i].radical || at[i].charge || bHasChargedNeighbor( at, i ))
            {
                continue;
            }
            /* Looking for tautomeric =O */
            if (1 != at[i].valence || BOND_TYPE_DOUBLE != at[i].bond_type[0] || at[i].num_H ||
                 2 != get_endpoint_valence( at[i].el_number ))
            {
                continue;
            }
            centp = at[i].neighbor[0];
            if (at[centp].sb_parity[0] || at[centp].p_parity || !is_centerpoint_elem( at[centp].el_number ))
            {
                continue;
            }
            /* Found a possible centerpoint, looking for -NH endpoint */
            for (k = 0; k < at[centp].valence; k++)
            {
                if (at[centp].bond_type[k] != BOND_TYPE_SINGLE)
                {
                    continue;
                }
                endp2 = at[centp].neighbor[k];
                if (at[endp2].endpoint != at[i].endpoint ||
                     !at[endp2].num_H || at[endp2].charge ||
                     at[endp2].sb_parity[0] || at[endp2].p_parity ||
                     at[endp2].valence != at[endp2].chem_bonds_valence ||
                     3 != at[endp2].chem_bonds_valence + at[endp2].num_H ||
                     3 != get_endpoint_valence( at[endp2].el_number ))
                {
                    continue;
                }
                /* Find bonds in reciprocal ajacency lists */
                for (i0 = 0; i0 < at[centp].valence && i != at[centp].neighbor[i0]; i0++)
                {
                    ;
                }
                for (k0 = 0; k0 < at[endp2].valence && centp != at[endp2].neighbor[k0]; k0++)
                {
                    ;
                }
                if (i0 == at[centp].valence || k0 == at[endp2].valence)
                {
                    return RI_ERR_PROGR;
                }
                /* -NH has been found */
                /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
                type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 1 ); /* subtract at[endp2] */

                at[i].bond_type[0] --;
                at[centp].bond_type[i0] --;
                at[i].chem_bonds_valence--;
                at[i].charge--;

                at[endp2].bond_type[k0] ++;
                at[centp].bond_type[k] ++;
                at[endp2].chem_bonds_valence++;
                at[endp2].num_H--;

                num_prot++;
                nNumSuccess++;

                /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add at[i] */
                type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 0 ); /* add at[endp2] */
            }
        }
    }

    if (num_prot > 0)
    {
        /* Add protons */
        /* 1. Use test from SimpleRemoveAcidicProtons() to test whether addition of H(+) to =C-O(-), etc. is correct */
        for (i = 0; i < num_atoms && num_prot && nAtTypeTotals[ATTOT_TOT_CHARGE] - nNumProtAddedByRestr >= 0; i++)
        {
            /* Choose an atom */
            if (at[i].sb_parity[0] || at[i].p_parity || at[i].num_H ||
                 at[i].charge != -1 || at[i].radical || bHasChargedNeighbor( at, i ))
            {
                continue;
            }
            /* Try to add a proton and check whether InChI would remove it back */
            at[i].charge++;
            at[i].num_H++;
            type = GetAtomChargeType( at, i, NULL, &mask, 0 );
            at[i].charge--;
            at[i].num_H--;
            if (type)
            {
                for (bSuccess = 0, j = 0; j < max_j_Ar; j++)
                {   
                    if ((bSuccess = ( type & ArTypMask[2 * j] ) && ( mask && ArTypMask[2 * j + 1] ))) /* djb-rwth: addressing LLVM warning */
                    {
                        break;
                    }
                }
                if (bSuccess)
                {
                    /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                    type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
                    at[i].charge++;
                    at[i].num_H++;
                    type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
                    num_prot--; /* success */
                    nNumSuccess++;
                }
            }
        }
        /* 2. Use test from SimpleRemoveHplusNPO() */
        for (i = 0; i < num_atoms && num_prot; i++)
        {
            /* Choose an atom */
            if (at[i].sb_parity[0] || at[i].p_parity ||
                 at[i].charge || at[i].radical || bHasChargedNeighbor( at, i ))
            {
                continue;
            }
            /* Try to add a proton and check whether InChI would remove it back */
            at[i].num_H++;
            at[i].charge++;
            bSuccess = ( PR_SIMPLE_TYP & ( type = GetAtomChargeType( at, i, NULL, &mask, 0 ) ) ) &&
                ( PR_SIMPLE_MSK & mask ); /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
            at[i].num_H--;  /* failed */
            at[i].charge--;
            if (bSuccess)
            {
                /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
                at[i].num_H++;
                at[i].charge++;
                type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
                num_prot--;     /* succeeded */
                nNumSuccess++;
            }
        }
    }

    if (num_prot < 0 && ( bNormalizationFlags & FLAG_PROTON_AC_HARD_ADDED ) && 1 == num_tg &&
         nAtTypeTotals[ATTOT_TOT_CHARGE] - nNumProtAddedByRestr <= 0)
    {
        /* Try to remove protons from tautomeric N (specific ADP must be present) */
        int nNumAcceptors_DB_O = 0, nNumDonors_SB_NH = 0, num_max, num_success;
        for (i = 0; i < num_atoms; i++)
        {
            /* Choose an atom */
            if (!at[i].endpoint || at[i].radical ||
                 at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ))
            {
                continue;
            }
            type = GetAtomChargeType( at, i, NULL, &mask, 0 );
            if (( type & AA_HARD_TYP_CO ) && ( mask & AA_HARD_MSK_CO ))
            {
                nNumAcceptors_DB_O++;
            }
            else
            {
                if (( type == ATT_ATOM_N ) && ( mask == ATBIT_NP_H ) && !at[i].charge &&
                     at[i].valence == at[i].chem_bonds_valence)
                {
                    nNumDonors_SB_NH++;
                }
            }
        }
        num_max = inchi_min( nNumAcceptors_DB_O, nNumDonors_SB_NH );
        for (i = 0, num_success = 0; i < num_atoms && num_success < num_max && num_prot < 0; i++)
        {
            /* Choose an atom */
            if (!at[i].endpoint || at[i].radical || at[i].sb_parity[0] ||
                 at[i].p_parity || bHasChargedNeighbor( at, i ))
            {
                continue;
            }
            type = GetAtomChargeType( at, i, NULL, &mask, 0 );
            if (( type == ATT_ATOM_N ) && ( mask == ATBIT_NP_H ) && !at[i].charge &&
                 at[i].valence == at[i].chem_bonds_valence)
            {
                /* djb-rwth: ignoring LLVM warning: variable used to store function return value */
                type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
                at[i].num_H--;
                at[i].charge--;
                type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
                num_prot++;
                num_success++;
                nNumSuccess++;
            }
        }
    }

    /*exit_function:*/

    *num_protons_to_add = num_prot;

    return ret < 0 ? ret : nNumSuccess;
}


/****************************************************************************
Add or remove isotopic protons
****************************************************************************/
int AddRemoveIsoProtonsRestr( inp_ATOM *at,
                              int num_atoms,
                              NUM_H num_protons_to_add[],
                              int num_tg )
{
    int i, j, k, n, ret = 0;
    int   nNumSuccess = 0, min_at, max_at, num_H, num_iso_H, num_expl_H, num_expl_iso_H; /* djb-rwth: ignoring LLVM warning: possible presence of global variables */
    int   iCurIso; /* 0=> 1H, 1=> D, 2=> T */
    int   iCurMode, iCurMode1, iCurMode2; /* 0=> Not Endpoints, 1=> Endpoints */

                                          /* Distribute isotopes from  heaviest to lightest; pick up atoms in order 1. Not endpoints; 2. Endpoints */
    iCurMode1 = 0;
    iCurMode2 = num_tg ? 1 : 0;
    for (iCurMode = iCurMode1; iCurMode <= iCurMode2; iCurMode++)
    {
        for (iCurIso = 2; 0 <= iCurIso; iCurIso--)
        {
            /* check for isotopic H to add */
            if (!num_protons_to_add[iCurIso])
            {
                continue;
            }
            if (0 > num_protons_to_add[iCurIso])
            {
                ret = RI_ERR_PROGR;
                goto exit_function;
            }

            /* Limits for atom scanning */
            min_at = 0;
            max_at = num_atoms;

            /* Cycle withio the limits */
            for (i = min_at; i < max_at && 0 < num_protons_to_add[iCurIso]; i++)
            {
                /* Pick an atom */
                if (iCurMode)
                {
                    if (at[i].endpoint)
                    {
                        j = i;  /* atom number */
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    if (!at[i].endpoint && 1 == bHeteroAtomMayHaveXchgIsoH( at, i ))
                    { /* atom number */
                        j = i;
                    }
                    else
                    {
                        if (at[i].el_number == EL_NUMBER_H && at[i].charge == 1 &&
                             !at[i].valence && !at[i].radical && !at[i].iso_atw_diff)
                        {
                            /* proton, not isotopic; make it isotopic */
                            at[i].iso_atw_diff = 1 + iCurIso;
                            num_protons_to_add[iCurIso] --;
                            nNumSuccess++;
                            continue;
                        }
                        else
                        {
                            continue;
                        }
                    }
                }

                /* j is the atom number */
                /* count implicit H */
                num_H = at[j].num_H;
                num_iso_H = NUM_ISO_H(at, j); /* djb-rwth: ignoring LLVM warning: possible presence of global variables */

                while (num_H > 0 && num_protons_to_add[iCurIso] > 0)
                {
                    /* Substitute one implicit H with an isotopic atom H */
                    at[j].num_iso_H[iCurIso] ++;
                    at[j].num_H--;
                    num_protons_to_add[iCurIso] --;
                    num_H--;
                    num_iso_H++;
                    nNumSuccess++;
                }
                /* Count explicit H */
                num_expl_H = num_expl_iso_H = 0;
                for (k = 0; k < at[j].valence && num_atoms <= ( n = at[j].neighbor[k] ); k++)
                {
                    num_expl_H += ( 0 == at[n].iso_atw_diff );
                    num_expl_iso_H += ( 0 != at[n].iso_atw_diff );
                }
                while (num_expl_H > 0 && num_protons_to_add[iCurIso] > 0)
                {
                    /* Substitute one explicit H with an isotopic atom H */
                    n = at[j].neighbor[num_expl_H];
                    if (at[n].iso_atw_diff)
                    {
                        ret = RI_ERR_PROGR;
                        goto exit_function;
                    }
                    at[n].iso_atw_diff = 1 + iCurIso;
                    num_expl_H--;
                    num_expl_iso_H++;
                    num_protons_to_add[iCurIso] --;
                    nNumSuccess++;
                }
            }
        }
    }

exit_function:

    return ret < 0 ? ret : nNumSuccess;
}

#endif
