third_party/inchi/INCHI_BASE/src/ichirvr4.c
int CheckAndRefixStereobonds( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
                              inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
                              int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask )
{
    int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK;
    int inv_forbidden_edge_stereo = ~forbidden_edge_stereo;

    int i, k, ne, j1, j2, num_wrong, num_fixed;
    int ret2, retBNS, ret;
    int num_at = pStruct->num_atoms;
    int num_deleted_H = pStruct->num_deleted_H;
    int len_at = num_at + num_deleted_H;
    EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges;

    BNS_EDGE   *pEdge;
    Vertex      v1, v2;
    BNS_VERTEX *pv1, *pv2;

    ret = 0;

    /* to simplify, prepare new at[] from pBNS */
    memcpy( at2, at, len_at * sizeof( at2[0] ) );
    pStruct->at = at2;
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
    pStruct->at = at;
    if (ret2 < 0)
    {
        return ret;
    }

    num_wrong = 0;
    /* find wrong double bonds */
    for (i = 0; i < num_at; i++)
    {
        if (at2[i].valence == 3 &&
             at2[i].chem_bonds_valence - at2[i].valence == 1 &&
             at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
             ( at2[i].bond_type[j1 = (int) at2[i].sb_ord[0]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE &&
             ( at2[i].bond_type[j2 = (int) at2[i].sb_ord[1]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE  &&
             j1 != j2)
        {

            num_wrong++;
        }
    }
    if (!num_wrong)
    {
        return 0;
    }
    num_fixed = 0;
    for (i = 0; i < pBNS->num_bonds; i++)
    {
        pEdge = pBNS->edge + i;
        if (pEdge->forbidden & forbidden_edge_stereo)
        {
            num_fixed++;
        }
    }

    /* there may be no fixed stereo bonds at all, see #87607 */
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
    AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR );
    AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR );

    /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */

    if (0 > ( ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ) ))
    {
        goto exit_function;
    }
    if (( ret = AllocEdgeList( &FixedEdges, num_fixed ) ) ||
        ( ret = AllocEdgeList( &WrongEdges, num_wrong ) ))
    {
        goto exit_function;
    }
    /* collect wrong double bonds and set flow=0 */
    for (i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i++)
    {
        if (at2[i].valence == 3 &&
             at2[i].chem_bonds_valence - at2[i].valence == 1 &&
             at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
             ( at2[i].bond_type[j1 = (int) at2[i].sb_ord[0]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE &&
             ( at2[i].bond_type[j2 = (int) at2[i].sb_ord[1]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE &&
             j1 != j2)
        {
            switch (j1 + j2)
            {
                case 1: /* 0, 1 */
                    k = 2;
                    break;
                case 2: /* 0, 2 */
                    k = 1;
                    break;
                case 3: /* 1, 2 */
                    k = 0;
                    break;
                default:
                    ret = RI_ERR_PROGR;
                    goto exit_function;
            }
            ne = pBNS->vert[i].iedge[k];
            pEdge = pBNS->edge + ne;
            v1 = pEdge->neighbor1;
            v2 = pEdge->neighbor12 ^ v1;
            pv1 = pBNS->vert + v1;
            pv2 = pBNS->vert + v2;

            if (!pEdge->flow)
            {
                ret = RI_ERR_PROGR;
                goto exit_function;
            }
            pEdge->flow--;
            pEdge->forbidden |= forbidden_edge_mask;
            pv1->st_edge.flow--;
            pv2->st_edge.flow--;
            pBNS->tot_st_flow -= 2;
            if (ret = AddToEdgeList( &WrongEdges, ne, 0 ))
            {
                goto exit_function;
            }
        }
    }
    /* remove forbidden mark from stereo bonds (unfix stereo bonds) */
    for (i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i++)
    {
        pEdge = pBNS->edge + i;
        if (pEdge->forbidden & forbidden_edge_stereo)
        {
            pEdge->forbidden &= inv_forbidden_edge_stereo;
            FixedEdges.pnEdges[FixedEdges.num_edges++] = i;
        }
    }
    /* Run BNS to move charges and rearrange bond orders */
    retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
    ( *pnNumRunBNS )++;
    if (retBNS < 0)
    {
        goto exit_function;
    }
    else
    {
        if (retBNS > 0)
        {
            *pnTotalDelta += retBNS;
        }
    }
    /* remove forbidden_edge_mask and set forbidden_edge_stereo */
    RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask );
    /* allow carbon charges to change */
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
    /* fix previously unfixed stereo bonds */
    SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo );
    /* Run BNS again in case not all edge flows are maximal */
    ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
    ( *pnNumRunBNS )++;
    if (ret2 < 0)
    {
        goto exit_function;
    }
    else
    {
        if (ret2 > 0)
        {
            *pnTotalDelta += retBNS;
        }
    }
    ret = retBNS;

exit_function:

    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
    AllocEdgeList( &FixedEdges, EDGE_LIST_FREE );
    AllocEdgeList( &WrongEdges, EDGE_LIST_FREE );

    return ret;
}
/****************************************************************************
 Find A=X< where all bonds to X except A=X are marked as stereogenic;
 temporary allow stereobonds  change and make A=X bonds single                                                                   */
Find A=X< where all bonds to X except A=X are marked as stereogenic;
temporary allow stereobonds  change and make A=X bonds single
Find A = X< where all bonds to X except A = X are marked as stereogenic ; temporary allow stereobonds change and make A = X bonds single
int CheckAndRefixStereobonds( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
                              inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
                              int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask )
{
    int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK;
    int inv_forbidden_edge_stereo = ~forbidden_edge_stereo;
    int i, k, ne, j1, j2, num_wrong, num_fixed;
    int ret2, retBNS, ret;
    int num_at = pStruct->num_atoms;
    int num_deleted_H = pStruct->num_deleted_H;
    int len_at = num_at + num_deleted_H;
    EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges;
    BNS_EDGE   *pEdge;
    Vertex      v1, v2;
    BNS_VERTEX *pv1, *pv2;
    ret = 0;
    memcpy( at2, at, len_at * sizeof( at2[0] ) );
    pStruct->at = at2;
    ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
    pStruct->at = at;
    if (ret2 < 0)
    {
        return ret;
    }
    num_wrong = 0;
    for (i = 0; i < num_at; i++)
    {
        if (at2[i].valence == 3 &&
             at2[i].chem_bonds_valence - at2[i].valence == 1 &&
             at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
             ( at2[i].bond_type[j1 = (int) at2[i].sb_ord[0]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE &&
             ( at2[i].bond_type[j2 = (int) at2[i].sb_ord[1]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE  &&
             j1 != j2)
        {
            num_wrong++;
        }
    }
    if (!num_wrong)
    {
        return 0;
    }
    num_fixed = 0;
    for (i = 0; i < pBNS->num_bonds; i++)
    {
        pEdge = pBNS->edge + i;
        if (pEdge->forbidden & forbidden_edge_stereo)
        {
            num_fixed++;
        }
    }
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
    AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR );
    AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR );
    if (0 > ( ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ) ))
    {
        goto exit_function;
    }
    if (( ret = AllocEdgeList( &FixedEdges, num_fixed ) ) ||
        ( ret = AllocEdgeList( &WrongEdges, num_wrong ) ))
    {
        goto exit_function;
    }
    for (i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i++)
    {
        if (at2[i].valence == 3 &&
             at2[i].chem_bonds_valence - at2[i].valence == 1 &&
             at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
             ( at2[i].bond_type[j1 = (int) at2[i].sb_ord[0]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE &&
             ( at2[i].bond_type[j2 = (int) at2[i].sb_ord[1]] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE &&
             j1 != j2)
        {
            switch (j1 + j2)
            {
                case 1: 
                    k = 2;
                    break;
                case 2: 
                    k = 1;
                    break;
                case 3: 
                    k = 0;
                    break;
                default:
                    ret = RI_ERR_PROGR;
                    goto exit_function;
            }
            ne = pBNS->vert[i].iedge[k];
            pEdge = pBNS->edge + ne;
            v1 = pEdge->neighbor1;
            v2 = pEdge->neighbor12 ^ v1;
            pv1 = pBNS->vert + v1;
            pv2 = pBNS->vert + v2;
            if (!pEdge->flow)
            {
                ret = RI_ERR_PROGR;
                goto exit_function;
            }
            pEdge->flow--;
            pEdge->forbidden |= forbidden_edge_mask;
            pv1->st_edge.flow--;
            pv2->st_edge.flow--;
            pBNS->tot_st_flow -= 2;
            if (ret = AddToEdgeList( &WrongEdges, ne, 0 ))
            {
                goto exit_function;
            }
        }
    }
    for (i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i++)
    {
        pEdge = pBNS->edge + i;
        if (pEdge->forbidden & forbidden_edge_stereo)
        {
            pEdge->forbidden &= inv_forbidden_edge_stereo;
            FixedEdges.pnEdges[FixedEdges.num_edges++] = i;
        }
    }
    retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
    ( *pnNumRunBNS )++;
    if (retBNS < 0)
    {
        goto exit_function;
    }
    else
    {
        if (retBNS > 0)
        {
            *pnTotalDelta += retBNS;
        }
    }
    RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask );
    RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
    SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo );
    ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
    ( *pnNumRunBNS )++;
    if (ret2 < 0)
    {
        goto exit_function;
    }
    else
    {
        if (ret2 > 0)
        {
            *pnTotalDelta += retBNS;
        }
    }
    ret = retBNS;
exit_function:
    AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
    AllocEdgeList( &FixedEdges, EDGE_LIST_FREE );
    AllocEdgeList( &WrongEdges, EDGE_LIST_FREE );
    return ret;
}
int CheckAndRefixStereobonds ( BN_STRUCT * pBNS , BN_DATA * pBD , StrFromINChI * pStruct , inp_ATOM * at , inp_ATOM * at2 , VAL_AT * pVA , ALL_TC_GROUPS * pTCGroups , int * pnNumRunBNS , int * pnTotalDelta , int forbidden_edge_mask ) { int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK ; int inv_forbidden_edge_stereo = ~ forbidden_edge_stereo ; int i , k , ne , j1 , j2 , num_wrong , num_fixed ; int ret2 , retBNS , ret ; int num_at = pStruct -> num_atoms ; int num_deleted_H = pStruct -> num_deleted_H ; int len_at = num_at + num_deleted_H ; EDGE_LIST FixedEdges , WrongEdges , CarbonChargeEdges ; BNS_EDGE * pEdge ; Vertex v1 , v2 ; BNS_VERTEX * pv1 , * pv2 ; ret = 0 ; memcpy ( at2 , at , len_at * sizeof ( at2 [ 0 ] ) ) ; pStruct -> at = at2 ; ret2 = CopyBnsToAtom ( pStruct , pBNS , pVA , pTCGroups , 1 ) ; pStruct -> at = at ; if ( ret2 < 0 ) { return ret ; } num_wrong = 0 ; for ( i = 0 ; i < num_at ; i ++ ) { if ( at2 [ i ] . valence == 3 && at2 [ i ] . chem_bonds_valence - at2 [ i ] . valence == 1 && at2 [ i ] . sb_parity [ 0 ] && at2 [ i ] . sb_parity [ 1 ] && ! at2 [ i ] . sb_parity [ 2 ] && ( at2 [ i ] . bond_type [ j1 = ( int ) at2 [ i ] . sb_ord [ 0 ] ] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE && ( at2 [ i ] . bond_type [ j2 = ( int ) at2 [ i ] . sb_ord [ 1 ] ] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE && j1 != j2 ) { num_wrong ++ ; } } if ( ! num_wrong ) { return 0 ; } num_fixed = 0 ; for ( i = 0 ; i < pBNS -> num_bonds ; i ++ ) { pEdge = pBNS -> edge + i ; if ( pEdge -> forbidden & forbidden_edge_stereo ) { num_fixed ++ ; } } AllocEdgeList ( & CarbonChargeEdges , EDGE_LIST_CLEAR ) ; AllocEdgeList ( & FixedEdges , EDGE_LIST_CLEAR ) ; AllocEdgeList ( & WrongEdges , EDGE_LIST_CLEAR ) ; if ( 0 > ( ret = ForbidCarbonChargeEdges ( pBNS , pTCGroups , & CarbonChargeEdges , forbidden_edge_mask ) ) ) { goto exit_function ; } if ( ( ret = AllocEdgeList ( & FixedEdges , num_fixed ) ) || ( ret = AllocEdgeList ( & WrongEdges , num_wrong ) ) ) { goto exit_function ; } for ( i = 0 ; i < num_at && WrongEdges . num_edges < num_wrong ; i ++ ) { if ( at2 [ i ] . valence == 3 && at2 [ i ] . chem_bonds_valence - at2 [ i ] . valence == 1 && at2 [ i ] . sb_parity [ 0 ] && at2 [ i ] . sb_parity [ 1 ] && ! at2 [ i ] . sb_parity [ 2 ] && ( at2 [ i ] . bond_type [ j1 = ( int ) at2 [ i ] . sb_ord [ 0 ] ] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE && ( at2 [ i ] . bond_type [ j2 = ( int ) at2 [ i ] . sb_ord [ 1 ] ] & BOND_TYPE_MASK ) == BOND_TYPE_SINGLE && j1 != j2 ) { switch ( j1 + j2 ) { case 1 : k = 2 ; break ; case 2 : k = 1 ; break ; case 3 : k = 0 ; break ; default : ret = RI_ERR_PROGR ; goto exit_function ; } ne = pBNS -> vert [ i ] . iedge [ k ] ; pEdge = pBNS -> edge + ne ; v1 = pEdge -> neighbor1 ; v2 = pEdge -> neighbor12 ^ v1 ; pv1 = pBNS -> vert + v1 ; pv2 = pBNS -> vert + v2 ; if ( ! pEdge -> flow ) { ret = RI_ERR_PROGR ; goto exit_function ; } pEdge -> flow -- ; pEdge -> forbidden |= forbidden_edge_mask ; pv1 -> st_edge . flow -- ; pv2 -> st_edge . flow -- ; pBNS -> tot_st_flow -= 2 ; if ( ret = AddToEdgeList ( & WrongEdges , ne , 0 ) ) { goto exit_function ; } } } for ( i = 0 ; i < pBNS -> num_bonds && FixedEdges . num_edges < num_fixed ; i ++ ) { pEdge = pBNS -> edge + i ; if ( pEdge -> forbidden & forbidden_edge_stereo ) { pEdge -> forbidden &= inv_forbidden_edge_stereo ; FixedEdges . pnEdges [ FixedEdges . num_edges ++ ] = i ; } } retBNS = RunBnsRestoreOnce ( pBNS , pBD , pVA , pTCGroups ) ; ( * pnNumRunBNS ) ++ ; if ( retBNS < 0 ) { goto exit_function ; } else { if ( retBNS > 0 ) { * pnTotalDelta += retBNS ; } } RemoveForbiddenEdgeMask ( pBNS , & WrongEdges , forbidden_edge_mask ) ; RemoveForbiddenEdgeMask ( pBNS , & CarbonChargeEdges , forbidden_edge_mask ) ; SetForbiddenEdgeMask ( pBNS , & FixedEdges , forbidden_edge_stereo ) ; ret2 = RunBnsRestoreOnce ( pBNS , pBD , pVA , pTCGroups ) ; ( * pnNumRunBNS ) ++ ; if ( ret2 < 0 ) { goto exit_function ; } else { if ( ret2 > 0 ) { * pnTotalDelta += retBNS ; } } ret = retBNS ; exit_function : AllocEdgeList ( & CarbonChargeEdges , EDGE_LIST_FREE ) ; AllocEdgeList ( & FixedEdges , EDGE_LIST_FREE ) ; AllocEdgeList ( & WrongEdges , EDGE_LIST_FREE ) ; return ret ; }
Find A=X< where all bonds to X except A=X are marked as stereogenic;
temporary allow stereobonds  change and make A=X bonds single
Find A = X< where all bonds to X except A = X are marked as stereogenic ; temporary allow stereobonds change and make A = X bonds single
/* to simplify, prepare new at[] from pBNS */ /* find wrong double bonds */ /* there may be no fixed stereo bonds at all, see #87607 */ /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */ /* collect wrong double bonds and set flow=0 */ /* 0, 1 */ /* 0, 2 */ /* 1, 2 */ /* remove forbidden mark from stereo bonds (unfix stereo bonds) */ /* Run BNS to move charges and rearrange bond orders */ /* remove forbidden_edge_mask and set forbidden_edge_stereo */ /* allow carbon charges to change */ /* fix previously unfixed stereo bonds */ /* Run BNS again in case not all edge flows are maximal */