/*
 * International Chemical Identifier (InChI)
 * Version 1
 * Software version 1.05
 * January 27, 2017
 *
 * 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.
 *
 * IUPAC/InChI-Trust Licence No.1.0 for the
 * International Chemical Identifier (InChI)
 * Copyright (C) IUPAC and InChI Trust Limited
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0,
 * or any later version.
 *
 * Please note that this library is distributed WITHOUT ANY WARRANTIES
 * whatsoever, whether expressed or implied.
 * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details.
 *
 * You should have received a copy of the IUPAC/InChI Trust InChI
 * Licence No. 1.0 with this library; if not, please write to:
 *
 * The InChI Trust
 * 8 Cavendish Avenue
 * Cambridge CB1 7US
 * UK
 *
 * or e-mail to alan@inchi-trust.org
 *
 */


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

#include "mode.h"

#include "ichister.h"
#include "ichimain.h"
#include "ichimake.h"
#include "ichi_io.h"


/*
    Local functions
*/

static int str_LineStart( const char *tag, char *tag2, int val2,
                          INCHI_IOSTREAM_STRING *strbuf, int ind );
static int str_LineEnd( const char *tag, int *bOverflow,
                        INCHI_IOSTREAM_STRING *buf,
                        int ind, int bPlainTextTags );

static int CleanOrigCoord( MOL_COORD szCoord, int delim );
static int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i,
                           char *szBuf, int buf_len);
static int WriteOrigAtoms( CANON_GLOBALS *pCG, int num_inp_atoms, inp_ATOM *at, int *i,
                           char *szBuf, int buf_len, STRUCT_DATA *sd);
static int WriteOrigBonds( CANON_GLOBALS *pCG, int num_inp_atoms, inp_ATOM *at, int *i,
                           char *szBuf, int buf_len,
                           STRUCT_DATA *sd);

static void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2);

static int Output_RecordInfo( INCHI_IOSTREAM *out_file, int num_input_struct,
                              int bNoStructLabels, const char *szSdfLabel,
                              const char *szSdfValue, long lSdfId, char *pLF, char *pTAB );
static int OutputINCHI_VersionAndKind( INCHI_IOSTREAM *out_file,
                                       INCHI_IOSTREAM_STRING *strbuf,
                                       int bINChIOutputOptions,
                                       int is_beta, char *pLF, char *pTAB );
static int OutputINCHI_MainLayerFormula( CANON_GLOBALS *pCG,
                                         INCHI_IOSTREAM *out_file,
                                         INCHI_IOSTREAM_STRING *strbuf,
                                         int num_components2[],
                                         int *INCHI_basic_or_INCHI_reconnected,
                                         OutputINCHI_CtlData *io,
                                         char *pLF, char *pTAB );
static int OutputINCHI_MainLayerConnections( CANON_GLOBALS *pCG,
                                             INCHI_IOSTREAM *out_file,
                                             INCHI_IOSTREAM_STRING *strbuf,
                                             int num_components2[],
                                             int *INCHI_basic_or_INCHI_reconnected,
                                             OutputINCHI_CtlData *io,
                                             char *pLF, char *pTAB );
static int OutputINCHI_MainLayerHydrogens( CANON_GLOBALS *pCG,
                                           INCHI_IOSTREAM *out_file,
                                           INCHI_IOSTREAM_STRING *strbuf,
                                           int num_components2[],
                                           int *INCHI_basic_or_INCHI_reconnected,
                                           OutputINCHI_CtlData *io,
                                           char *pLF, char *pTAB );
static int OutputINCHI_ChargeAndRemovedAddedProtonsLayers( CANON_GLOBALS *pCG,
                                                           INCHI_IOSTREAM *out_file,
                                                           INCHI_IOSTREAM_STRING *strbuf,
                                                           OutputINCHI_CtlData *io,
                                                           char *pLF, char *pTAB );
static int OutputINCHI_StereoLayer( CANON_GLOBALS *pCG,
                                    INCHI_IOSTREAM *out_file,
                                    INCHI_IOSTREAM_STRING *strbuf,
                                    OutputINCHI_CtlData *io,
                                    char *pLF, char *pTAB );
static int OutputINCHI_IsotopicLayer( CANON_GLOBALS *pCG,
                                      INCHI_IOSTREAM *out_file, INCHI_IOSTREAM_STRING *strbuf,
                                      int *INCHI_basic_or_INCHI_reconnected,
                                      OutputINCHI_CtlData *io,
                                      char *pLF, char *pTAB );
static int OutputINCHI_FixedHLayerWithSublayers( CANON_GLOBALS *pCG,
                                                 INCHI_IOSTREAM *out_file,
                                                 INCHI_IOSTREAM_STRING *strbuf,
                                                 int *INCHI_basic_or_INCHI_reconnected,
                                                 OutputINCHI_CtlData *io,
                                                 char *pLF, char *pTAB,
                                                 int *then_goto_repeat );
static int OutputINCHI_PolymerLayer( CANON_GLOBALS *pCG,
                                     INCHI_IOSTREAM *out_file,
                                     INCHI_IOSTREAM_STRING *strbuf,
                                     int *INCHI_basic_or_INCHI_reconnected,
                                     ORIG_STRUCT *pOrigStruct,
                                     OutputINCHI_CtlData *io,
                                     char *pLF, char *pTAB );
static int OutputAUXINFO_HeaderAndNormalization_type( CANON_GLOBALS *pCG,
                                                      INCHI_IOSTREAM *out_file,
                                                      INCHI_IOSTREAM_STRING *strbuf,
                                                      int bINChIOutputOptions,
                                                      int *INCHI_basic_or_INCHI_reconnected,
                                                      int num_components2[],
                                                      OutputINCHI_CtlData *io,
                                                      char *pLF, char *pTAB );
static int OutputAUXINFO_OriginalNumbersAndEquivalenceClasses( CANON_GLOBALS *pCG,
                                                               INCHI_IOSTREAM *out_file,
                                                               INCHI_IOSTREAM_STRING *strbuf,
                                                               int num_components2[],
                                                               OutputINCHI_CtlData *io,
                                                               char *pLF, char *pTAB );
static int OutputAUXINFO_TautomericGroupsEquivalence( CANON_GLOBALS *pCG,
                                                      INCHI_IOSTREAM *out_file,
                                                      INCHI_IOSTREAM_STRING *strbuf,
                                                      OutputINCHI_CtlData *io,
                                                      char *pLF, char *pTAB );
static int OutputAUXINFO_Stereo( CANON_GLOBALS *pCG,
                                  INCHI_IOSTREAM *out_file,
                                  INCHI_IOSTREAM_STRING *strbuf,
                                  OutputINCHI_CtlData *io,
                                  char *pLF, char *pTAB );
static int OutputAUXINFO_IsotopicInfo( CANON_GLOBALS *pCG,
                                       INCHI_IOSTREAM *out_file,
                                       INCHI_IOSTREAM_STRING *strbuf,
                                       int *INCHI_basic_or_INCHI_reconnected,
                                       OutputINCHI_CtlData *io,
                                       char *pLF, char *pTAB );
static int OutputAUXINFO_Transposition( CANON_GLOBALS *pCG,
                                        INCHI_IOSTREAM *out_file,
                                        INCHI_IOSTREAM_STRING *strbuf,
                                        int *INCHI_basic_or_INCHI_reconnected,
                                        OutputINCHI_CtlData *io,
                                        char *pLF, char *pTAB );
static int OutputAUXINFO_ChargesRadicalsAndUnusualValences( CANON_GLOBALS *pCG,
                                                            INCHI_IOSTREAM *out_file,
                                                            INCHI_IOSTREAM_STRING *strbuf,
                                                            OutputINCHI_CtlData *io,
                                                            char *pLF, char *pTAB );
static int OutputAUXINFO_ReversibilityInfo( CANON_GLOBALS *pCG,
                                            INCHI_IOSTREAM *out_file,
                                            INCHI_IOSTREAM_STRING *strbuf,
                                            ORIG_STRUCT *pOrigStruct,
                                            OutputINCHI_CtlData *io,
                                            char *pLF, char *pTAB );

static int OutputAUXINFO_PolymerInfo( CANON_GLOBALS *pCG,
                                      INCHI_IOSTREAM *out_file,
                                      INCHI_IOSTREAM_STRING *strbuf,
                                      ORIG_STRUCT *pOrigStruct,
                                      OutputINCHI_CtlData *io,
                                      char *pLF, char *pTAB );
int psbond_min_num_compare( int *bond1, int* bond2 );

void HidePolymerRelatedInternals( INCHI_IOSTREAM *out );
int CountZzInFormula( char *s);



/*
    Local constants
*/
const char sCompDelim[]       = ";"; /* component delimiter */
const char sIdenticalValues[] = "*"; /* identical component */
const char x_space[]          = "                  ";


/*
    Output: words & additional tags
*/
const char x_inchi[]          = INCHI_NAME;
const char x_inchi_ver[]      = "version"; /* "InChI.version"; */
const char x_curr_ver[]       = INCHI_VERSION;
const char x_structure[]      = "structure";
const char x_number[]         = "number";
const char x_header[]         = "id.name";
const char x_value[]          = "id.value";
const char x_empty[]          = "";
const char x_type[]           = "type";
const char x_message[]        = "message";
const char x_text[]           = "value";
const char x_ferr[]           = "fatal (aborted)";
const char x_err[]            = "error (no InChI)";
const char x_warn[]           = "warning";
const char x_basic[]          = "identifier";
const char x_tautomeric[]     = "mobile-H";
const char x_reconnected[]    = "reconnected";
const char x_ver[]            = "version";
const char x_type_alpha[]     = "alpha";
const char x_type_numer[]     = "numeric";
const char x_type_predec[]    = "sct";
const char x_type_normal[]    = "normal";
const char x_type_short[]     = "compressed";
const char x_basic_layer[]    = "basic";
const char x_aux_basic[]      = "identifier.auxiliary-info";
const char x_aux_comm[]       = "!-- This section is NOT a part of the identifier, it is not unique --";
const char x_ign_uu_sp2[]     = "omit_undef_dbond";
const char x_ign_uu_sp3[]     = "omit_undef_sp3";
const char x_line_opening[]   = "<";
const char x_line_closing[]   = "</";
const char x_close_line[]     = ">";
const char x_abs[]            = "1";
const char x_rel[]            = "2";
const char x_rac[]            = "3";

typedef struct tagInchiTag
{
    const char *szPlainLabel;
    const char *szPlainComment;
    const char *szXmlLabel;
    int  bAlwaysOutput;
} INCHI_TAG;


/*
    Identifier
*/
const INCHI_TAG IdentLbl[] =
{
                                                                  /* prefixes: may be combined in this order */
/* IL_FIXH_ORD, */    { "/",   "fixed_H",        "fixed-H",        0 }, /* fixed H */
/* IL_ISOT_ORD, */    { "/",   "isotopic",       "isotopic",       0 }, /* isotopic */
/* IL_STER_ORD, */    { "/",   "stereo",         "stereo",         0 }, /* stereo */
                                                                   /* items */
/* IL_VERS_ORD, */    { "" ,   "version",        "version",        1 },
/* IL_FML__ORD, */    { "/",   "formula",        "formula",        1 }, /* basic part formula */
/* IL_CONN_ORD, */    { "/c",  "connections",    "connections",    1 },
/* IL_ALLH_ORD, */    { "/h",  "H_atoms",        "H",              1 },
/* IL_CHRG_ORD, */    { "/q",  "charge",         "charge",         1 },
/* IL_PROT_ORD, */    { "/p",  "protons",        "protons",        0 },
                                                                    /* stereo */
/* IL_DBND_ORD, */    { "/b",  "dbond",          "dbond",          0 },
/* IL_SP3S_ORD, */    { "/t",  "sp3",            "sp3",            0 },
/* IL_INVS_ORD, */    { "/m",  "sp3:inverted",   "abs.inverted",   0 }, /* mirrored */
/* IL_TYPS_ORD, */    { "/s",  "type (1=abs, 2=rel, 3=rac)", "type",           0 }, /* stereo type */
                                                                    /* isotopic */
/* IL_ATMS_ORD, */    { "/i",  "atoms",          "atoms",          1 },
                                                                    /* isotopic mobile H only */
/* IL_XCGA_ORD, */    { "/h",  "exchangeable_H", "H-isotopic",     1 },
                                                                    /* fixed H only */
/* IL_FMLF_ORD, */    { "/f",  "formula",        "formula",        1 }, /* fixed H formula */
/* IL_HFIX_ORD, */    { "/h",  "H_fixed" ,       "H-fixed" ,       1 }, /* fixed-H */
/* IL_TRNS_ORD, */    { "/o",  "transposition",  "transposition",  0 }, /* order */
/* IL_REC__ORD, */    { "/r",  "reconnected bond(s) to metal(s) formula",  "formula",  0 }
};

/*

  Parsing plain text InChI (FML is a chemical formula)
  ========================

  1.12Beta/FML       /i      /f[FML]  /i   [/o] /rFML      /i      /f[FML]  /i   [/o] end
          |          |       |        |         |          |       |        |         |
Labels    | chqpbtms | hbtms | hqbtms | btms    | chqpbtms | hbtms | hqbtms | btms    |
inside:   |          |       |        |         |          |       |        |         |
          | non-iso- | iso-  | fix-   | iso-    | non-iso- | iso-  | fix-   | iso-    |
meaning:  | topic    | topic | ed H   | topic   | topic    | topic | ed H   | topic   |
          |----------+-------+--------+---------|----------+-------+--------+---------|
          |        mobile-H  |   fixed-H        |        mobile-H  |   fixed-H        |
          |----------+-------+--------+---------|----------+-------+--------+---------|
          |                                     |                                     |
          |     normal  or disconected metal    |      reconnected bonds to metal     |
          |_____________________________________|_____________________________________|

  meanings of h:

       /h   -  immobile H & mobile H group(s)
     /i/h   -  exchangeable isotopic H (common)
     /f/h   -  fixed-H
     /f/i/h -  never happens

*/
typedef enum tagIdentLblOrd
{
    IL_FIXH_ORD,
    IL_ISOT_ORD,
    IL_STER_ORD,

    IL_VERS_ORD,
    IL_FML__ORD,
    IL_CONN_ORD,
    IL_ALLH_ORD,
    IL_CHRG_ORD,
    IL_PROT_ORD,

    IL_DBND_ORD,
    IL_SP3S_ORD,
    IL_INVS_ORD,
    IL_TYPS_ORD,

    IL_ATMS_ORD,

    IL_XCGA_ORD,

    IL_FMLF_ORD,
    IL_HFIX_ORD,
    IL_TRNS_ORD,
    IL_REC__ORD,

    IL_MAX_ORD /* max number of tags */
} IDENT_LBL_ORD;


typedef enum tagIdentLblBit
{
    IL_FIXH = 1 << IL_FIXH_ORD,
    IL_ISOT = 1 << IL_ISOT_ORD,
    IL_STER = 1 << IL_STER_ORD,

    IL_VERS = 1 << IL_VERS_ORD,
    IL_FML_ = 1 << IL_FML__ORD,
    IL_CONN = 1 << IL_CONN_ORD,
    IL_ALLH = 1 << IL_ALLH_ORD,
    IL_CHRG = 1 << IL_CHRG_ORD,
    IL_PROT = 1 << IL_PROT_ORD,

    IL_DBND = 1 << IL_DBND_ORD,
    IL_SP3S = 1 << IL_SP3S_ORD,
    IL_INVS = 1 << IL_INVS_ORD,
    IL_TYPS = 1 << IL_TYPS_ORD,

    IL_ATMS = 1 << IL_ATMS_ORD,

    IL_XCGA = 1 << IL_XCGA_ORD,

    IL_FMLF = 1 << IL_FMLF_ORD,
    IL_HFIX = 1 << IL_HFIX_ORD,
    IL_TRNS = 1 << IL_TRNS_ORD,
    IL_REC_ = 1 << IL_REC__ORD
} IDENT_LBL_BIT;



/*
    Aux Info constants
*/
const INCHI_TAG AuxLbl[] =
{
                                                                    /* prefixes may be combined in this order */
/* AL_FIXH_ORD, */    { "/",     "fixed_H",                "fixed-H",             0 }, /* fixed-H */
/* AL_ISOT_ORD, */    { "/",     "isotopic",               "isotopic",            0 }, /* isotopic */
/* AL_STER_ORD, */    { "/",     "abs_stereo_inverted",    "stereo.abs.inverted", 0 }, /* inv abs sp3 stereo */
/* AL_REVR_ORD, */    { "/",     "reversibility",          "reversibility",       0 }, /* reversibility */
                                                                       /* items */
/* AL_VERS_ORD, */    { "",      "version",                "version",             1 },
/* AL_NORM_ORD, */    { "/",     "normalization_type",     "norm-type",           1 },
/* AL_ANBR_ORD, */    { "/N:",   "original_atom_numbers",  "atom.orig-nbr",       1 },
/* AL_AEQU_ORD, */    { "/E:",   "atom_equivalence",       "atom.equivalence",    0 },
/* AL_GEQU_ORD, */    { "/gE:",  "group_equivalence",      "group.equivalence",   0 },
                                                                       /* inv abs sp3 stereo */
/* AL_SP3I_ORD, */    { "/it:",  "sp3",                    "sp3",                 0 },
/* AL_SP3N_ORD, */    { "/iN:",  "original_atom_numbers",  "atom.orig-nbr",       0 },

/* AL_CRV__ORD, */    { "/CRV:", "charge_radical_valence", "charges-rad-val",     0 },
                                                                       /* reversibility */
/* AL_ATMR_ORD, */    { "/rA:",  "atoms",                  "atoms",               0 },
/* AL_BNDR_ORD, */    { "/rB:",  "bonds",                  "bonds",               0 },
/* AL_XYZR_ORD, */    { "/rC:",  "xyz",                    "xyz",                 0 },
                                                                       /* fixed-H only */
/* AL_FIXN_ORD, */    { "/F:",   "original_atom_numbers",  "atom.orig-nbr",       1 },
                                                                       /* isotopic only */
/* AL_ISON_ORD, */    { "/I:",   "original_atom_numbers",  "atom.orig-nbr",       1 },

/* AL_REC__ORD, */    { "/R:",  "reconnected bond(s) to metal(s) part",  "",      1 }
};


typedef enum tagAuxLblOrd
{
    AL_FIXH_ORD,
    AL_ISOT_ORD,
    AL_STER_ORD,
    AL_REVR_ORD,

    AL_VERS_ORD,
    AL_NORM_ORD,
    AL_ANBR_ORD,
    AL_AEQU_ORD,
    AL_GEQU_ORD,

    AL_SP3I_ORD,
    AL_SP3N_ORD,

    AL_CRV__ORD,

    AL_ATMR_ORD,
    AL_BNDR_ORD,
    AL_XYZR_ORD,

    AL_FIXN_ORD,

    AL_ISON_ORD,

    AL_REC__ORD,

    AL_MAX_ORD   /* max number of tags */
} AUX_LBL_ORD;


typedef enum tagAuxLblBit
{
    AL_FIXH = 1 << AL_FIXH_ORD,
    AL_ISOT = 1 << AL_ISOT_ORD,
    AL_STER = 1 << AL_STER_ORD,
    AL_REVR = 1 << AL_REVR_ORD,

    AL_VERS = 1 << AL_VERS_ORD,
    AL_NORM = 1 << AL_NORM_ORD,
    AL_ANBR = 1 << AL_ANBR_ORD,
    AL_AEQU = 1 << AL_AEQU_ORD,
    AL_GEQU = 1 << AL_GEQU_ORD,

    AL_SP3I = 1 << AL_SP3I_ORD,
    AL_SP3N = 1 << AL_SP3N_ORD,

    AL_CRV_ = 1 << AL_CRV__ORD,

    AL_ATMR = 1 << AL_ATMR_ORD,
    AL_BNDR = 1 << AL_BNDR_ORD,
    AL_XYZR = 1 << AL_XYZR_ORD,

    AL_FIXN = 1 << AL_FIXN_ORD,

    AL_ISON = 1 << AL_ISON_ORD,

    AL_REC_ = 1 << AL_REC__ORD
} AUX_LBL_BIT;

const int MAX_TAG_NUM = inchi_max((int)IL_MAX_ORD, (int)AL_MAX_ORD);

char *szGetTag(const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways);

#define SP(N)        (x_space+sizeof(x_space)-1-(N))



#define NOT_YET_I2I_FOR_POLYMERS 40


/*
    Print error message (plain text)
*/
int OutputINChIPlainError( INCHI_IOSTREAM *out_file,
                           char *pErrorText,
                           int bError )
{
    /* char szBuf[64]; */
    const char *pErr;
    char *szErrorText = pErrorText;
    int nEstLen, ret = 0;

    switch( bError ) {
    case _IS_WARNING:
        pErr = x_warn;
        break;
    case _IS_ERROR:
        pErr = x_err;
        break;
    default: /*  _IS_FATAL */
        pErr = x_ferr;
        break;
    }
                  /* <%s: >, x_message */
    nEstLen =     (int) (sizeof(x_message)-1 + 1 + 1
                  /* <%s=\"%s\">, x_type, pErr */
                  + sizeof(x_type)-1 + 1 + 1 + strlen(pErr) + 1
                  /* < %s=\"%s\"\n>, x_text, szErrorText */
                  + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 1 + 1);

    inchi_ios_print( out_file,
                     "%s: %s=\"%s\" %s=\"%s\"",
                     x_message, x_type, pErr, x_text, szErrorText );
    ret = 1;

    return ret;
}


#ifndef OUT_TN    /* defined in mode.h; quoted here for reference purposes only */

#define OUT_N1              0    /* non-tautomeric only */
#define OUT_T1              1    /* tautomeric if present otherwise non-tautomeric */
#define OUT_NT              2    /* only non-taut representations of tautomeric */
#define OUT_TN              3    /* tautomeric if present otherwise non-tautomeric;
                                    sepatately output non-taut representations of tautomeric if present */
/* OUT_TN = OUT_T1 + OUT_NT */
#endif


/*
    Calculate equivalence mark
    (used to check for repeating (sub)layer(s) )
*/
const char *EquString( int EquVal )
{
    int bFrom = EquVal & (iiSTEREO | iiSTEREO_INV | iiNUMB | iiEQU );
    int bType = EquVal & (iitISO   | iitNONTAUT );
    int bEq2  = EquVal & (iiEq2NONTAUT | iiEq2ISO | iiEq2INV );
    const char *r = "";

#if ( FIX_EMPTY_LAYER_BUG == 1 )
    int bEmpty= EquVal & iiEmpty;
    if ( bEmpty ) {
        r = "e";
        return r;
    }
#endif

    switch ( bFrom ) {

    case iiSTEREO:  /* ------------ Stereo --------------------*/
        switch ( bType ) {
        case iitISO:  /* iso main stereo =... */
            switch( bEq2 ) {
            case 0:
                r = "m";            /* iso main stereo = main stereo */
                break;
            default:
                r = "??";           /* should not happen */
                break;
            }
            break;
        case iitNONTAUT: /* non-taut stereo =... */
            switch( bEq2 ) {
            case 0:
                r = "m";            /* non-taut stereo = main stereo */
                break;
            default:
                r = "??";           /* should not happen */
                break;
            }
            break;
        case (iitNONTAUT | iitISO): /* iso non-taut stereo = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";            /* iso non-taut stereo = main stereo */
                break;
            case iiEq2ISO:
                r = "M";            /* iso non-taut stereo = main iso stereo */
                break;
            case iiEq2NONTAUT:
                r = "n";            /* iso non-taut stereo = non-taut stereo */
                break;
            default:
                r = "??";           /* should not happen */
                break;
            }
            break;
        default:
            r = "??";           /* should not happen */
            break;
        }
        break;

    case iiSTEREO_INV: /*---------- Inverted Aux Stereo ------*/
        if ( bEq2 & iiEq2INV ) { /* stereo = Inverted(another stereo) */
            bEq2 &= ~iiEq2INV;
            switch( bType ) {
            case 0: /* main = ...*/
                switch( bEq2 ) {
                case 0:
                    r = "im";       /* main         = Inv(main) */
                    break;
                case iiEq2ISO:
                    r = "iM";       /* main         = Inv(main iso) */
                    break;
                case iiEq2NONTAUT:
                    r = "in";       /* maim         = Inv(non-taut) */
                    break;
                case (iiEq2NONTAUT | iiEq2ISO):
                    r = "iN";       /* maim         = Inv(non-taut iso ) */
                    break;
                default:
                    r = "??";           /* should not happen */
                    break;
                }
                break;
            case iitISO: /* main iso = ...*/
                switch( bEq2 ) {
                case 0:
                    r = "im";       /* main iso     = Inv(main) */
                    break;
                case iiEq2ISO:
                    r = "iM";       /* main iso     = Inv(main iso) */
                    break;
                case iiEq2NONTAUT:
                    r = "in";       /* maim iso     = Inv(non-taut) */
                    break;
                case (iiEq2NONTAUT | iiEq2ISO):
                    r = "iN";       /* maim         = Inv(non-taut iso ) */
                    break;
                default:
                    r = "??";           /* should not happen */
                    break;
                }
                break;
            case iitNONTAUT: /* non-taut = ... */
                switch( bEq2 ) {
                case 0:
                    r = "im";       /* non-taut     = Inv(main) */
                    break;
                case iiEq2ISO:
                    r = "iM";       /* non-taut     = Inv(main iso) */
                    break;
                case iiEq2NONTAUT:
                    r = "in";       /* non-taut     = Inv(non-taut) */
                    break;
                case (iiEq2NONTAUT | iiEq2ISO):
                    r = "iN";       /* non-taut     = Inv(non-taut iso ) */
                    break;
                default:
                    r = "??";           /* should not happen */
                    break;
                }
                break;
            case (iitNONTAUT | iitISO):
                switch( bEq2 ) {
                case 0:
                    r = "im";       /* non-taut iso = Inv(main) */
                    break;
                case iiEq2ISO:
                    r = "iM";       /* non-taut iso = Inv(main iso) */
                    break;
                case iiEq2NONTAUT:
                    r = "in";       /* non-taut iso = Inv(non-taut) */
                    break;
                case (iiEq2NONTAUT | iiEq2ISO):
                    r = "iN";       /* non-taut iso = Inv(non-taut iso ) */
                    break;
                default:
                    r = "??";           /* should not happen */
                }
                break;
            default:
                r = "??";           /* should not happen */
                break;
            }
        } else {  /* Inv stereo = another (non-inverted) stereo */

            switch( bType ) {
            case iitISO: /* main iso = ...*/
                switch( bEq2 ) {
                case 0:
                    r = "m";       /* main         = (inverted aux) main */
                    break;
                default:
                    r = "??";           /* should not happen */
                    break;
                }
                break;
            case iitNONTAUT: /* non-taut = ... */
                switch( bEq2 ) {
                case 0:
                    r = "m";       /* non-taut     = (inverted aux) main */
                    break;
                default:
                    r = "??";           /* should not happen */
                    break;
                }
                break;
            case (iitNONTAUT | iitISO): /* non-taut iso = ...*/
                switch( bEq2 ) {
                case 0:
                    r = "m";        /* non-taut iso  = (inverted aux) main */
                    break;
                case iiEq2ISO:
                    r = "M";       /* non-taut iso  = (inverted aux) main iso */
                    break;
                case iiEq2NONTAUT:
                    r = "n";       /* non-taut iso  = (inverted aux) non-taut */
                    break;
                default:
                    r = "??";           /* should not happen */
                    break;
                }
                break;
            default:
                r = "??";           /* should not happen */
                break;
            }
        }
        break;

    case ( iiNUMB | iiSTEREO_INV): /*------------- Inv Stereo Numbering ------------*/
        switch( bType ) {
        case 0: /* inv stereo numb main = ...*/
            switch( bEq2 ) {
            case 0:
                r = "m";       /* inv stereo numb main     = main numb */
                break;
            default:
                r = "??";      /* should not happen */
                break;
            }
            break;
        case iitISO: /* inv stereo iso numb main = ...*/
            switch( bEq2 ) {
            case 0:
                r = "m";       /* inv stereo iso numb main = main numb  */
                break;
            case iiEq2INV:
                r = "im";      /* inv stereo iso numb main = InvStereo(main) numb */
                break;
            case iiEq2ISO:
                r = "M";      /* inv stereo iso numb main = isotopic main numb */
                break;
            default:
                r = "??";      /* should not happen */
                break;
            }
            break;
        case iitNONTAUT: /* inv stereo numb non-taut = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";       /* inv stereo numb non-taut = main numb */
                break;
            case iiEq2NONTAUT:
                r = "n";       /* inv stereo numb non-taut = non-taut numb */
                break;
            case iiEq2INV:
                r = "im";      /* inv stereo numb non-taut =  InvStereo(main) numb  */
                break;
            default:
                r = "??";      /* should not happen */
                break;
            }
            break;
        case (iitNONTAUT | iitISO): /* inv stereo numb non-taut iso = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";       /* inv stereo numb non-taut iso = main numb */
                break;
            case iiEq2ISO:
                r = "M";       /* inv stereo numb non-taut iso = main numb iso */
                break;
            case (iiEq2ISO | iiEq2INV):
                r = "iM";       /* inv stereo numb non-taut iso = InvStereo(main iso) numb */
                break;
            case iiEq2NONTAUT:
                r = "n";       /* inv stereo numb non-taut iso = non-taut numb */
                break;
            case (iiEq2NONTAUT | iiEq2ISO):
                r = "N";       /* inv stereo numb non-taut iso = non-taut iso numb */
                break;
            case iiEq2INV:
                r = "im";      /* inv stereo numb non-taut iso = InvStereo(main) numb */
                break;
            case (iiEq2NONTAUT | iiEq2INV):
                r = "in";      /* inv stereo numb non-taut iso = InvStereo(non-taut) numb ) */
                break;
            default:
                r = "??";           /* should not happen  */
                break;
            }
            break;
        default:
            r = "??";           /* should not happen */
            break;
        }
        break;

    case iiNUMB:           /*------------- Canonical Numbering ------------*/
        switch( bType ) {
        case 0:         /* numb main = ...*/
            r = "??";      /* should not happen */
            break;
        case iitISO:     /* iso numb main = ...*/
            switch( bEq2 ) {
            case 0:
                r = "m";       /* iso numb main = main numb  */
                break;
            default:
                r = "??";      /* should not happen */
            }
            break;
        case iitNONTAUT: /* numb non-taut = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";       /* numb non-taut = main numb */
                break;
            default:
                r = "??";      /* should not happen */
            }
            break;
        case (iitNONTAUT | iitISO): /* numb non-taut iso = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";       /* numb non-taut iso = main numb */
                break;
            case iiEq2ISO:
                r = "M";       /* numb non-taut iso = main numb iso */
                break;
            case iiEq2NONTAUT:
                r = "n";       /* numb non-taut iso = non-taut numb */
                break;
            default:
                r = "??";           /* should not happen */
                break;
            }
            break;
        default:
            r = "??";           /* should not happen */
            break;
        }
        break;

    case iiEQU:         /*------------- Atom Equivalence ------------*/
        switch( bType ) {
        case 0:         /* equivalence main = ...*/
            r = "??";      /* should not happen */
            break;
        case iitISO:     /* equivalence main iso = ...*/
            switch( bEq2 ) {
            case 0:
                r = "m";       /* equivalence main = main equ  */
                break;
            default:
                r = "??";      /* should not happen */
                break;
            }
            break;
        case iitNONTAUT: /* equivalence non-taut = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";       /* equivalence non-taut = main equ */
                break;
            default:
                r = "??";      /* should not happen */
                break;
            }
            break;
        case (iitNONTAUT | iitISO): /*  equivalence non-taut iso = ... */
            switch( bEq2 ) {
            case 0:
                r = "m";       /* equivalence non-taut iso = main equ */
                break;
            case iiEq2ISO:
                r = "M";       /* equivalence non-taut iso = main iso equ */
                break;
            case iiEq2NONTAUT:
                r = "n";       /* equivalence non-taut iso = non-taut equ */
                break;
            default:
                r = "??";      /* should not happen */
                break;
            }
            break;
        default:
            r = "??";          /* should not happen */
            break;
        }
        break;
    default:
        r = "??";      /* should not happen */
        break;
    }
    return r;
}


#define OUT_NONTAUT  OUT_NN  /* was OUT_NT until 2004-04-07 */


/*
    OutputINChI2( ... )

    called from SortAndPrintINChI( ... )
*/
int OutputINChI2( CANON_GLOBALS *pCG,
                  INCHI_IOSTREAM_STRING *strbuf,
                  INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM],
                  int INCHI_basic_or_INCHI_reconnected,
                  ORIG_STRUCT *pOrigStruct,
                  int bDisconnectedCoord,
                  int bOutputType,
                  int bINChIOutputOptions,
                  int bAbcNumbers,
                  int bCtPredecessors,
                  int bNoStructLabels,
                  int num_components2[],
                  int num_non_taut2[],
                  int num_taut2[],
                  INCHI_IOSTREAM *output_file,
                  INCHI_IOSTREAM *log_file,
                  int num_input_struct,
                  const char *szSdfLabel,
                  const char *szSdfValue,
                  long lSdfId,
                  int *pSortPrintINChIFlags,
                  unsigned char save_opt_bits)
{
    int bINChIOutputOptions0 = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS);
    int bINChIOutputOptionsCur;
    int bCurOption, ret, i;

    ret = 0;

    for ( i = 0; i < 3; i ++ )
    {
        switch( i )
        {
        case 1:
            bCurOption = INCHI_OUT_PLAIN_TEXT;
            break;
        case 2:
            bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS;
            break;
        default:
            continue;
        }
        if ( bINChIOutputOptions & bCurOption )
        {
            bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption;
            if ( i != 1 )
            {
                bINChIOutputOptionsCur  &= ~INCHI_OUT_TABBED_OUTPUT;
            }
            ret |= OutputINChI1( pCG,
                                 strbuf,
                                 pINChISortTautAndNonTaut2,
                                 INCHI_basic_or_INCHI_reconnected,
                                 pOrigStruct,
                                 bDisconnectedCoord, bOutputType, bINChIOutputOptionsCur,
                                 bAbcNumbers, bCtPredecessors, bNoStructLabels,
                                 num_components2, num_non_taut2, num_taut2,
                                 output_file, log_file, num_input_struct,
                                 szSdfLabel, szSdfValue, lSdfId,
                                 pSortPrintINChIFlags, save_opt_bits);
        }
    }

    return ret;
}


/*
    OutputINChI1( ... )

    Main actual worker which serializes InChI to string.

    Called from OutputINChI2( ... ) and from itself

*/
int OutputINChI1( CANON_GLOBALS *pCG,
                  INCHI_IOSTREAM_STRING *strbuf,
                  INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM],
                  int INCHI_basic_or_INCHI_reconnected,
                  ORIG_STRUCT *pOrigStruct,
                  int bDisconnectedCoord,
                  int bOutputType,
                  int bINChIOutputOptions,
                  int bAbcNumbers,
                  int bCtPredecessors,
                  int bNoStructLabels,
                  int num_components2[],
                  int num_non_taut2[],
                  int num_taut2[],
                  INCHI_IOSTREAM *out_file,
                  INCHI_IOSTREAM *log_file,
                  int num_input_struct,
                  const char *szSdfLabel,
                  const char *szSdfValue,
                  long lSdfId,
                  int *pSortPrintINChIFlags,
                  unsigned char save_opt_bits)
{
    OutputINCHI_CtlData io;


    /*

    bINChIOutputOptions bits:

        INCHI_OUT_NO_AUX_INFO           0x0001    do not output Aux Info
        INCHI_OUT_SHORT_AUX_INFO        0x0002    output short version of Aux Info
        INCHI_OUT_ONLY_AUX_INFO         0x0004    output only Aux Info
        INCHI_OUT_EMBED_REC             0x0008    embed reconnected INChI into disconnected INChI

    */


    /*
        bOutputType =
         TAUT_YES  => tautomeric only (if no tautomeric components then no output;
         TAUT_NON  => only non-tautomeric output (if no non-taut present then no output;
         TAUT_BOTH => tautomeric and non-tautomeric
    */
    int  i, j, ii, jj, /*ii2, jj2,*/ bEmbeddedOutputCalled=0;
    int  bTautIsoHNum, bTautIsoAt, bHasIsotopicAtoms[TAUT_NUM];
    int  bStereoSp2[TAUT_NUM], bStereoSp3[TAUT_NUM];
    int  bIsotopicStereoSp2[TAUT_NUM], bIsotopicStereoSp3[TAUT_NUM];
    int  bStereoAbsInverted[TAUT_NUM], bIsotopicStereoAbsInverted[TAUT_NUM];
    int  bStereoAbs[TAUT_NUM], bIsotopicStereoAbs[TAUT_NUM];
    int  bTautomericAcid, bHardAddRemProton;
    int  bRequestedRacemicStereo=0, bRequestedRelativeStereo = 0;
    int  ind, inc, npass = 0;

    INCHI_SORT   *is, *is2;
    INChI        *pINChI /*, *pINChI2*/;
    INChI_Aux    *pINChI_Aux = NULL;

    int  ret = 0; /*  0=>failed, 1=>success */
    int  intermediate_result = 0;
    int then_goto_repeat = 0;
    int  max_num_comp;
    int  bHasIsoH;
    int  nNumMovedProtons;
    int  bTautAndNonTaut, bTautIsNonTaut;
    int nAtomsAllComp1, nAtomsAllComp2;    /* v. 1.05 Total atoms in all components */

    int  bPlainText        = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS));
    int  bPlainTextCommnts = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS);

    char *pLF, *pTAB;

#ifdef TARGET_LIB_FOR_WINCHI
    /* @@@ From now on we will go with silent output; it ends on @@@ below */
    int silent = 1;
#endif

    int bFixTranspChargeBug = 0;
#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) /* 2008-01-02 */
    if ( INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG & bINChIOutputOptions )
        bFixTranspChargeBug = 1;
#endif

    io.bAbcNumbers = bAbcNumbers;

    io.ATOM_MODE = ((io.bAbcNumbers?CT_MODE_ABC_NUMBERS:0)
                    | CT_MODE_ATOM_COUNTS
                    | CT_MODE_NO_ORPHANS
#if ( EQL_H_NUM_TOGETHER == 1 )
                    | CT_MODE_EQL_H_TOGETHER
#endif
#if ( ABC_CT_NUM_CLOSURES == 1 )
                    | (io.bAbcNumbers && bCtPredecessors? CT_MODE_ABC_NUM_CLOSURES:0)
#endif
                    | (bCtPredecessors?CT_MODE_PREDECESSORS:0));

    io.TAUT_MODE                = (io.bAbcNumbers?CT_MODE_ABC_NUMBERS:0);
    io.pSortPrintINChIFlags        = pSortPrintINChIFlags;
    io.num_components            = num_components2[INCHI_basic_or_INCHI_reconnected];
    io.pINChISortTautAndNonTaut = pINChISortTautAndNonTaut2[INCHI_basic_or_INCHI_reconnected];
    io.pINChISort                = io.pINChISortTautAndNonTaut[TAUT_YES];
    io.pINChISort2                = io.pINChISortTautAndNonTaut[TAUT_YES];
    io.bAlways                    = 0;
    io.bUseMulipliers            = 1;
    io.bOmitRepetitions            = 1;
    io.bPlainTextTags            = 2;  /* 0 => no plain tags, 1=> plain text tags, 2=>plaintext tags without consecutive // */
    io.bOutputType                = bOutputType; /* remains constant */
    io.bOutType                    = bOutputType;    /* will change! */

    io.bSecondNonTautPass        = 0;
    io.bNonTautIsoIdentifierNotEmpty    = 0;
    io.bNonTautNonIsoIdentifierNotEmpty = 0;
    io.bNonTautIsIdenticalToTaut        = 1;
    io.bFhTag                    = 0;
    io.nTag  = bPlainTextCommnts? 3 : bPlainText? 2 : 0; /* tag type */

    ind                            =  -1;
    inc                            =  -1;



    set_line_separators( bINChIOutputOptions, &pLF, &pTAB );

    memset( io.sDifSegs, DIFV_BOTH_EMPTY, sizeof(io.sDifSegs) );

    if ( !strbuf || !(strbuf->pStr) || strbuf->nAllocatedLength<=0 )
    {
        inchi_ios_eprint(log_file,
                            "Cannot allocate output buffer. No output for structure #%d.%s%s%s%s\n",
                            num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue));
        return ret;
    }

    /*    -- commented out to allow empty InChI --
    if (!io.num_components ) return 0;
    */

    for ( i = 0; i < TAUT_NUM; i ++ )
    {
        bHasIsotopicAtoms[i]      = io.num_comp[i]                  =
        bStereoSp2[i]             = bStereoSp3[i]                =
        bIsotopicStereoSp2[i]     = bIsotopicStereoSp3[i]        =
        io.bIsotopicOrigNumb[i]      =
        bStereoAbs[i]             = bIsotopicStereoAbs[i]         =
        bStereoAbsInverted[i]     = bIsotopicStereoAbsInverted[i] =
        io.bRacemicStereo[i]      = io.bRelativeStereo[i]            =
        io.bIsotopicRacemicStereo[i] = io.bIsotopicRelativeStereo[i]    =
        io.bAtomEqu[i]            = io.bTautEqu[i]                   =
        io.bIsotopicAtomEqu[i]       = io.bIsotopicTautEqu[i]           =
        io.bInvStereo[i]             = io.bInvIsotopicStereo[i]         =
        io.bInvStereoOrigNumb[i]     = io.bInvIsotopicStereoOrigNumb[i] =
        io.bIgn_UU_Sp3[i]            = io.bIgn_UU_Sp2[i]                =
        io.bIgn_UU_Sp3_Iso[i]        = io.bIgn_UU_Sp2_Iso[i]            =
        io.bChargesRadVal[i]         = io.bOrigCoord[i]                 = 0;
    }

    /*    Find if it is isotopic */
    io.bIsotopic    = io.bTautomeric = io.bNonTautomeric = bTautomericAcid =
                      bHardAddRemProton = bTautIsoHNum = bTautIsoAt = 0;
    bTautAndNonTaut = bTautIsNonTaut = 0;

    /*
         x = bStereo, bStereoSp2, bStereoSp3, bStereoAbsInverted,
             bIsotopicStereo, bIsotopicStereoSp2, bIsotopicStereoSp3, bIsotopicStereoAbsInverted

         OUT_N1: x[TAUT_NON] refers to non-tautomeric only
         OUT_T1: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric
         OUT_NT: x[TAUT_NON] refers to non-taut representations of tautomeric
         OUT_TN: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric
                 x[TAUT_NON] refers to non-taut representations of tautomeric
     */

    memset( io.num_iso_H, 0, sizeof(io.num_iso_H) );
    io.nNumRemovedProtons = 0;
    nNumMovedProtons   = 0;
    bHasIsoH           = 0;
    io.bTautomericOutputAllowed = (io.bOutType==OUT_T1 || io.bOutType== OUT_TN);
    io.pINChISort =
        io.pINChISortTautAndNonTaut[io.bTautomericOutputAllowed? TAUT_YES : TAUT_NON];
    is  = io.pINChISort;
    is2 = (io.bOutType== OUT_TN)? io.pINChISortTautAndNonTaut[TAUT_NON] : NULL;


    for ( i = 0, is2 = io.pINChISortTautAndNonTaut[TAUT_NON];
          i < io.num_components;
          i++, is++, is2? is2++ : NULL )
    {

        CompINChILayers( is, is2, io.sDifSegs, bFixTranspChargeBug );

        io.bNonTautIsIdenticalToTaut =
            io.bNonTautIsIdenticalToTaut && !CompINChITautVsNonTaut(is, is2, 1);

        if ( is && (pINChI_Aux = is->pINChI_Aux[TAUT_YES]) )
        {
            for ( j = 0; j < NUM_H_ISOTOPES; j ++ )
            {
                bHasIsoH     += abs(pINChI_Aux->nNumRemovedIsotopicH[j]);
                io.num_iso_H[j] += pINChI_Aux->nNumRemovedIsotopicH[j];
            }
            io.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons;
            nNumMovedProtons   += abs(pINChI_Aux->nNumRemovedProtons);
        }

        if ( io.bTautomericOutputAllowed )
        {
            /* check for removed isotopic H */
            for ( j = TAUT_YES; j < TAUT_NUM; j ++ )
            {
                switch ( io.bOutType ) {
                case OUT_N1: /* x[TAUT_NON]: non-tautomeric only -- never happens */
                    jj = GET_II(io.bOutType,is);
                    if ( jj != j )
                        continue;
                    ii = TAUT_NON;
                    break;
                case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */
                    jj = GET_II(io.bOutType,is);
                    if ( jj != j )
                        continue;
                    ii = TAUT_YES;
                    break;
                case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric -- never happens */
                    jj = GET_II(io.bOutType,is);
                    if ( jj != j )
                        continue;
                    ii = TAUT_NON;
                    break;
                /* main path of control flow */
                case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric;
                              * x[TAUT_NON]: non-taut only if tautomeric is present */
                    jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1;
                    if ( jj == TAUT_YES )
                    {
                        /* Fix12 */
                        if ( is->pINChI[jj]->lenTautomer > 0 )
                        {
                            bTautAndNonTaut += (!is->pINChI[jj]->bDeleted && HAS_N(is));
                        } else
                        {
                            bTautIsNonTaut ++;
                        }
                    }
                    if ( jj < 0 )
                        continue;
                    ii = j;
                    break;
                default:
                    continue;
                }
                if ( jj != j )
                    continue;
                if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 && (pINChI_Aux = is->pINChI_Aux[jj]) )
                {
                    bTautIsoHNum += (pINChI_Aux->nNumRemovedIsotopicH[0] +
                                     pINChI_Aux->nNumRemovedIsotopicH[1] +
                                     pINChI_Aux->nNumRemovedIsotopicH[2]);
                    bTautIsoAt   += (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 );
                }
            }
        }
    }

    io.sDifSegs[DIFL_M ][DIFS_p_PROTONS] = io.nNumRemovedProtons? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;
    io.sDifSegs[DIFL_MI][DIFS_h_H_ATOMS] = bHasIsoH?           DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY;

    MarkUnusedAndEmptyLayers( io.sDifSegs );

    io.bNonTautIsIdenticalToTaut = io.bNonTautIsIdenticalToTaut && !bTautIsoHNum;


    nAtomsAllComp1 = nAtomsAllComp2 = 0;
    for ( i = 0, is = io.pINChISort; i < io.num_components; i ++, is ++ )
    {
        int bCurIso, bCurStereo, bCurIsoStereo, bCurHasIsoStereo /* Fix14 */, bCurTaut /*, bCurTaut2*/;
        int bCompExists, bCurIsoHPos, bCurIsoHStereo;
        int bCurStereoSp2, bCurIsoStereoSp2, bCurStereoSp3, bCurIsoStereoSp3, bCurIsoStereoSp3Inv;
        int bCurRacemic, bCurRelative, bCurIsoRacemic, bCurIsoRelative;
        bCompExists = 0;

        for ( j = TAUT_NON; j < TAUT_NUM; j ++ )
        {
            switch ( io.bOutType ) {
            case OUT_N1: /* x[TAUT_NON]: non-tautomeric only */
                jj = GET_II(io.bOutType,is);
                if ( jj != j )
                    continue;
                ii = TAUT_NON;
                break;
            case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */
                jj = GET_II(io.bOutType,is);
                if ( jj != j )
                    continue;
                ii = TAUT_YES;
                break;
            case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric */
                jj = GET_II(io.bOutType,is);
                if ( jj != j )
                    continue;
                ii = TAUT_NON;
                break;
            /* main control flow comes here: requested both mobile and fixed H results */
            case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric;
                          * x[TAUT_NON]: non-taut only if tautomeric is present */
                jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1;
                if ( jj < 0 )
                {
                    /* Fix12 */
                    if ( bTautAndNonTaut && bTautIsNonTaut &&
                         j == TAUT_NON && 0 <= (jj = GET_II(OUT_T1,is)) &&
                         !is->pINChI[jj]->bDeleted && !is->pINChI[jj]->lenTautomer )
                    {
                        ; /* the requested non-tautomeric component is in tautomeric position
                             (is->pINChI[TAUT_YES]);
                             process it also as non-tautomeric if Fixed-H layer was requested */
                    }
                    else
                    {
                        continue;
                    }
                }
                ii = j; /* ii is what we wanted; jj is what we found (0 = TAUT_NON: fixed_H, 1 = TAUT_YES: mobile_H) */
                /* -- not used 2004-09-16 ---
                if ( is2 ) {
                    jj2 = ( j == TAUT_YES )? GET_II(OUT_T1,is2) : ( j == TAUT_NON )? GET_II(OUT_NT,is2) : -1;
                    if ( jj2 >= 0 ) {
                        ii2 = j;
                    } else {
                        ii2 = -1;
                    }
                } else {
                    jj2 = ii2 = -1;
                }
                -----------------------------*/
                break;
            default:
                continue;
            }
            if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 )
            {
                /*pINChI_Aux = is->pINChI_Aux[jj];*/
                bCompExists++;

                if ( j == TAUT_NON )
                    nAtomsAllComp1+= pINChI->nNumberOfAtoms;
                else if ( j == TAUT_YES )
                    nAtomsAllComp2+= pINChI->nNumberOfAtoms;


                bCurTaut            = (pINChI->lenTautomer > 0);
                bCurIso             = (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 );
                bCurIsoHPos         = (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1 || pINChI->lenTautomer > 1);
                /* present isotopic H + their possible positions AND/OR isotopic atoms */
                bCurIsoHStereo      = bCurIsoHPos && (bTautIsoHNum || bTautIsoAt) || bCurIso;
                if ( jj == j && pINChI->bDeleted )
                {
                    io.num_comp[j] --;
                    if ( bCurTaut )
                    {
                        io.bTautomeric        |= 1; /* tautomeric representation is present */
                        io.bNonTautomeric     |= HAS_N(is);
                    }
                    io.bIsotopic    |= bCurIso;
                    continue; /* deleted H(+) in tautomeric representation */
                }
                bCurStereoSp2       = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoBonds > 0);
                bCurHasIsoStereo    =
                bCurStereoSp3       = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoCenters > 0 );
                bCurIsoStereoSp2    = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoBonds > 0);
                bCurIsoStereoSp3    = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0);
                bCurIsoStereoSp3Inv = bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs; /* inversion changes sp3 stereo */
                bRequestedRacemicStereo         |= (0!=(pINChI->nFlags & INCHI_FLAG_RAC_STEREO));

                bRequestedRelativeStereo        |= (0!=(pINChI->nFlags & INCHI_FLAG_REL_STEREO));
                /* check whether isotopic stereo is same as non-isotopic; if same than do not output isotopic stereo */
                if ( bCurStereoSp2 && bCurIsoStereoSp2 )
                {
                    bCurIsoStereoSp2 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP2, pINChI->StereoIsotopic, EQL_SP2, 0 );
                }
                if ( bCurStereoSp3 && bCurIsoStereoSp3 )
                {
                    /* bCurIsoStereoSp3=0 means (iso stereo sp3) = (non-iso stereo sp3) or (iso stereo sp3) = Inv(non-iso stereo sp3) */
                    bCurIsoStereoSp3 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP3, pINChI->StereoIsotopic, EQL_SP3,
                              (pINChI->nFlags & INCHI_FLAG_RAC_STEREO) || (pINChI->nFlags & INCHI_FLAG_REL_STEREO) );
                    if ( !bCurIsoStereoSp3 ) {
                        /* inversion changes iso sp3 differently from non-iso sp3 Fix11 */
                        bCurIsoStereoSp3Inv &= (pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs);
                    }
                }

                bCurRelative        =  bRequestedRelativeStereo && bCurStereoSp3;
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                bCurRelative        =  bCurRelative &&
                                      (pINChI->Stereo->nNumberOfStereoCenters > 1 ) &&
                                      (pINChI->Stereo->nCompInv2Abs != 0) &&
#endif



                bCurIsoRelative     = bRequestedRelativeStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv);
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                bCurIsoRelative     = bCurIsoRelative &&
                                      (pINChI->StereoIsotopic->nNumberOfStereoCenters > 1 ) &&
                                      (pINChI->StereoIsotopic->nCompInv2Abs != 0) &&
#endif


                bCurRacemic         = bRequestedRacemicStereo && bCurStereoSp3;
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                bCurRacemic         = bCurRacemic &&
                                      (pINChI->Stereo->nCompInv2Abs != 0) &&
                                      (pINChI->Stereo->nNumberOfStereoCenters > 0 ) ?
                                      pINChI->Stereo->nNumberOfStereoCenters : 0;
#endif

                bCurIsoRacemic      = bRequestedRacemicStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv);
#if ( REL_RAC_STEREO_IGN_1_SC == 1 )
                bCurIsoRacemic      = bCurIsoRacemic &
                                      (pINChI->StereoIsotopic->nCompInv2Abs != 0) &&
                                      (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0 ) ?
                                       pINChI->StereoIsotopic->nNumberOfStereoCenters : 0;
#endif
                if ( bRequestedRelativeStereo )
                {
                    bCurStereoSp3     = bCurRelative || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */
                    bCurIsoStereoSp3  = bCurIsoRelative   ? bCurIsoStereoSp3 : 0;
                }
                else
                {
                    if ( bRequestedRacemicStereo )
                    {
                        bCurStereoSp3     = bCurRacemic    > 1 || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */
                        bCurIsoStereoSp3  = bCurIsoRacemic > 1? bCurIsoStereoSp3 : 0;
                    }
                }
                bCurStereo          = bCurStereoSp2    || bCurStereoSp3;
                bCurIsoStereo       = bCurIsoStereoSp2 || bCurIsoStereoSp3;

                io.bIsotopic           |= bCurIso;
                bHasIsotopicAtoms[ii]  |= bCurIso;
                bStereoSp2[ii]         |= bCurStereoSp2;
                bStereoSp3[ii]         |= bCurStereoSp3;
                io.bIgn_UU_Sp3[ii]        |= !bCurStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_UU);
                io.bIgn_UU_Sp2[ii]        |= !bCurStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_UU);
                bIsotopicStereoSp2[ii] |= bCurIsoStereoSp2;
                bIsotopicStereoSp3[ii] |= bCurIsoStereoSp3;
                io.bIgn_UU_Sp3_Iso[ii]    |= !bCurIsoStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_ISO_UU);
                io.bIgn_UU_Sp2_Iso[ii]    |= !bCurIsoStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_ISO_UU);
                bStereoAbs[ii]                  |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs != 0);
                bStereoAbsInverted[ii]          |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs < 0);
                /* Fix08: missing isotopic inverted flag if isotopic = inverted non-isotopic */
                bIsotopicStereoAbsInverted[ii]  |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs < 0) ||
                                                   !bCurIsoStereoSp3  && pINChI->StereoIsotopic  && pINChI->Stereo &&
                                                   pINChI->StereoIsotopic->nCompInv2Abs &&
                                                   pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs;
                /* Fix 11: missing /s1 if only isotopic stereo is inverted */
                bIsotopicStereoAbs[ii]          |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs != 0) ||
                                                   !bCurIsoStereoSp3  && pINChI->StereoIsotopic  && pINChI->Stereo &&
                                                   pINChI->StereoIsotopic->nCompInv2Abs &&
                                                   pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs;

                io.bRelativeStereo[ii]             |= bCurRelative;
                io.bIsotopicRelativeStereo[ii]     |= bCurIsoRelative;
                io.bRacemicStereo[ii]              |= bCurRacemic;
                io.bIsotopicRacemicStereo[ii]      |= bCurIsoRacemic;


                bTautomericAcid                 |= (0!=(pINChI->nFlags & INCHI_FLAG_ACID_TAUT));
                bHardAddRemProton               |= (0!=(pINChI->nFlags & INCHI_FLAG_HARD_ADD_REM_PROTON));
                if ( bCurTaut )
                {
                    io.bTautomeric        |= 1; /* tautomeric representation is present */
                    /* does tautomeric structure have also a non-tautomeric repesentation? */
                    io.bNonTautomeric     |= HAS_N(is);
                }

                /* auxiliary info */
                if ( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) && (pINChI_Aux = is->pINChI_Aux[jj]) )
                {
                    /* detect presence of constitutional equivalence onfo */
                    int bCurEqu, bCurTautEqu=0, bCurIsoEqu=0, bCurIsoTautEqu=0; /* Fix15-disabled */
                    io.bAtomEqu[ii] |= (bCurEqu = bHasEquString( pINChI_Aux->nConstitEquNumbers,
                                                   pINChI_Aux->nNumberOfAtoms));
                    if ( bCurTaut )
                    {
                        io.bTautEqu[ii] |= (bCurTautEqu = bHasEquString( pINChI_Aux->nConstitEquTGroupNumbers,
                                                       pINChI_Aux->nNumberOfTGroups));
                    }
                    if ( bCurIso )
                    {
                        io.bIsotopicAtomEqu[ii] |= (bCurIsoEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicNumbers,
                                                               pINChI_Aux->nNumberOfAtoms)) /*|| bCurEqu*/;
                        if ( bCurTaut )
                        {
                            io.bIsotopicTautEqu[ii] |= (bCurIsoTautEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicTGroupNumbers,
                                                                   pINChI_Aux->nNumberOfTGroups)) /*|| bCurTautEqu*/;
                        }
                        /* non-zero if isotopic numbering for inverted isotopic stereo is different */
                        io.bIsotopicOrigNumb[ii] |= bCurHasIsoStereo && /* Fix14 */
                                                 pINChI_Aux->nOrigAtNosInCanonOrdInv &&
                                                 pINChI_Aux->nIsotopicOrigAtNosInCanonOrd &&
                            (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrdInv,
                                          pINChI_Aux->nIsotopicOrigAtNosInCanonOrd,
                                          sizeof(pINChI_Aux->nOrigAtNosInCanonOrdInv[0])
                                          * pINChI_Aux->nNumberOfAtoms));
                    }
                    /* inverted stereo */
                    if ( bCurStereoSp3 && pINChI->Stereo->nCompInv2Abs )
                    {
                        io.bInvStereo[ii]         |= 1;
                        io.bInvStereoOrigNumb[ii] |= pINChI_Aux->nOrigAtNosInCanonOrd &&
                                                  pINChI_Aux->nOrigAtNosInCanonOrdInv &&
                            (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrd,
                                          pINChI_Aux->nOrigAtNosInCanonOrdInv,
                                          sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0])
                                          * pINChI_Aux->nNumberOfAtoms));
                    }
                    /* inverted isotopic stereo */
                    if ( bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs )
                    {
                        io.bInvIsotopicStereo[ii]         |= 1;
                        io.bInvIsotopicStereoOrigNumb[ii] |= pINChI_Aux->nIsotopicOrigAtNosInCanonOrd &&
                                                          pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv &&
                            (0 != memcmp( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd,
                                          pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv,
                                          sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0])
                                          * pINChI_Aux->nNumberOfAtoms));
                    }
                    if ( pINChI_Aux->OrigInfo && bHasOrigInfo(pINChI_Aux->OrigInfo, pINChI_Aux->nNumberOfAtoms) )
                    {
                        io.bChargesRadVal[ii] |= 1;
                    }
                }
            }
        }
        if ( bCompExists )
        {
            for ( j = TAUT_NON; j < TAUT_NUM; j ++ )
            {
                io.num_comp[j] ++;
            }
        }
    }
    if ( io.bTautomeric /*&& bTautomericAcid*/ ) /* "&& bTautomericAcid" commented out 2004-06-02 */
    {
        io.bTautomeric += bTautomericAcid; /* long-range tautomerism */
        io.bTautomeric += (bHardAddRemProton? 4 : 0);
    }
    if ( bRequestedRacemicStereo || bRequestedRelativeStereo )
    {
        /* do not output inverted stereo info */
        for ( i = 0; i < TAUT_NUM; i ++ )
        {
            /* Fix11 */
            bStereoAbsInverted[i] =
            bStereoAbs[i]         =
            io.bInvStereo[i]         =
            io.bInvStereoOrigNumb[i] =  0;
            /* io.bIsotopicRelativeStereo[i]=0 may happen because iso stereo is same or inverted non-iso stereo */
            bIsotopicStereoAbsInverted[i] =
            bIsotopicStereoAbs[i]         =
            io.bInvIsotopicStereo[i]         =
            io.bInvIsotopicStereoOrigNumb[i] = 0;
        }
    }


    io.iCurTautMode = io.bOutType == OUT_N1? TAUT_NON:  /*  only non-taut                                    */
                   io.bOutType == OUT_T1? TAUT_YES:        /*  tautomeric if present, otherwise non-tautomeric */
                   io.bOutType == OUT_NT? TAUT_NON:        /*  only non-taut representations of tautomeric        */
                   io.bOutType == OUT_TN? TAUT_YES:        /*  tautomeric if present otherwise non-tautomeric; */
                                             -1;        /*  separately output non-taut representations of
                                                            tautomeric if present                            */

    if ( io.iCurTautMode < 0 )
    {
        return 0;  /* error */
    }


    /*
        Now print out
    */

    io.bOverflow = 0;
    io.num_components = io.num_comp[io.iCurTautMode];
    max_num_comp   = inchi_max(io.num_comp[TAUT_NON], io.num_comp[TAUT_YES]);

    if ( bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO )
    {
        goto output_aux_info;
    }

    io.nCurINChISegment = DIFL_M;


    /* Structure (Compound) Header */
    if ( INCHI_basic_or_INCHI_reconnected == INCHI_BAS )
        Output_RecordInfo( out_file, num_input_struct, bNoStructLabels, szSdfLabel, szSdfValue, lSdfId, pLF, pTAB );


    /* InChI output: version and kind */
    if ( INCHI_basic_or_INCHI_reconnected == INCHI_BAS || !(bINChIOutputOptions & INCHI_OUT_EMBED_REC))
    {
        int is_beta = 0;
        int nAtomsAllComp = inchi_max(nAtomsAllComp1, nAtomsAllComp2);

        if ( nAtomsAllComp > NORMALLY_ALLOWED_INP_MAX_ATOMS )
        {
            /* v. 1.05 for LargeMolecules */
            is_beta = 1;
        }
        if ( pOrigStruct && pOrigStruct->polymer )
        {
            /* v. 1.05 for Polymers */
            is_beta = 1;
        }
        OutputINCHI_VersionAndKind( out_file, strbuf, bINChIOutputOptions, is_beta, pLF, pTAB );
    }



    /* InChI output: atoms */
    intermediate_result    = OutputINCHI_MainLayerFormula( pCG, out_file, strbuf,  num_components2,
                                                        &INCHI_basic_or_INCHI_reconnected,
                                                        &io, pLF, pTAB );
    if ( intermediate_result != 0 )
        goto exit_function;

    /* InChI output: connection table */
    intermediate_result    = OutputINCHI_MainLayerConnections( pCG, out_file, strbuf, num_components2,
                                                            &INCHI_basic_or_INCHI_reconnected,
                                                            &io, pLF, pTAB );
    if ( intermediate_result != 0 )
        goto exit_function;

    /* InChI output: hydrogens (with tautomeric info) */
    intermediate_result = OutputINCHI_MainLayerHydrogens( pCG, out_file, strbuf, num_components2,
                                                          &INCHI_basic_or_INCHI_reconnected,
                                                          &io, pLF, pTAB );
    if ( intermediate_result != 0 )
        goto exit_function;

    io.bFhTag = 0;
    npass = 0;


repeat_INChI_output:

    /* InChI output: charge and  removed protons */
    intermediate_result = OutputINCHI_ChargeAndRemovedAddedProtonsLayers( pCG, out_file, strbuf,
                                                                          &io, pLF, pTAB);
    if ( intermediate_result != 0 )
        goto exit_function;

    /* InChI output: polymer layer */
    if ( npass == 0 )
    {
        intermediate_result    = OutputINCHI_PolymerLayer( pCG, out_file, strbuf,
                                                        &INCHI_basic_or_INCHI_reconnected,
                                                        pOrigStruct, &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;
    }

    /* InChI output: stereo (non-isotopic) */
    intermediate_result = OutputINCHI_StereoLayer( pCG,out_file, strbuf, &io, pLF, pTAB );
    if ( intermediate_result != 0 )
        goto exit_function;


    /* Switch from M to MI or from F to FI */
    io.nCurINChISegment ++;


    /* InChI output: isotopic */
    intermediate_result = OutputINCHI_IsotopicLayer( pCG, out_file, strbuf,
                                                     &INCHI_basic_or_INCHI_reconnected,
                                                     &io,pLF, pTAB );

    if ( intermediate_result != 0 )
        goto exit_function;



    /*
        At this point the INChI part of the output has been done.
        If this INChI is tautomeric and non-tautomeric results exist,
        then we need to output non-tautomeric data:
            fixed H
            stereo
            isotopic
            isotopic stereo
    */


    /* InChI output: FixedH and sublayers */
    intermediate_result = OutputINCHI_FixedHLayerWithSublayers( pCG, out_file, strbuf,
                                                                &INCHI_basic_or_INCHI_reconnected,
                                                                &io, pLF, pTAB, &then_goto_repeat );
    if ( intermediate_result != 0 )
        goto exit_function;
    if ( then_goto_repeat )
    {
        npass++;
        goto repeat_INChI_output;
    }


    /*
        InChI output:  reconnected structure
    */

    bEmbeddedOutputCalled = 0;
    if ( bDisconnectedCoord && INCHI_basic_or_INCHI_reconnected == INCHI_BAS &&
         (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && num_components2[INCHI_REC] )
    {
        int nRet;
        bEmbeddedOutputCalled = 1;

        /* output blank line before /R: in case of bPlainTextCommnts=1 */
        inchi_ios_print_nodisplay( out_file, "%s", pLF );
        /* end of disconnected INChI output */

        nRet = OutputINChI1( pCG, strbuf, pINChISortTautAndNonTaut2,
                             INCHI_REC, pOrigStruct,  0 /*bDisconnectedCoord*/,
                             bOutputType, bINChIOutputOptions | INCHI_OUT_NO_AUX_INFO,
                             bAbcNumbers, bCtPredecessors, bNoStructLabels,
                             num_components2, num_non_taut2, num_taut2,
                             out_file, log_file, num_input_struct,
                             szSdfLabel, szSdfValue, lSdfId,
                             pSortPrintINChIFlags, save_opt_bits);

        if ( !nRet )
            goto exit_function; /* error */
    }

    /* Here! Intercept point: edit polymer layer if it is present */
    if ( pOrigStruct && pOrigStruct->polymer )
    {
        HidePolymerRelatedInternals( out_file );
    }


    /* InChI output: save InChI creation options if requested */
    if ( !bEmbeddedOutputCalled                            &&
        ( bINChIOutputOptions & INCHI_OUT_SAVEOPT )        &&
        ( 0==(bINChIOutputOptions & INCHI_OUT_STDINCHI) )    /* not std-InChI output */
        )
    {
        char let1, let2;
        GetSaveOptLetters(save_opt_bits, &let1, &let2);
        inchi_ios_print_nodisplay( out_file, "\\%c%c", let1, let2 );
    }
    if ( !bEmbeddedOutputCalled && !bPlainTextCommnts )
    { /* plain text comment earlier ended with LF */
        inchi_ios_print_nodisplay( out_file, "%s%s",
                            (!num_components2[0] && !num_components2[1])? "//":"", /* empty InChI=// */
                            (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO)? "\n" : pTAB );
    /* end of INChI= output */
    }

    inchi_strbuf_reset( strbuf );

#ifdef TARGET_LIB_FOR_WINCHI
    /* @@@ Here we end up with silent output: display previously hidden output */
    if ( inchi_ios_flush_not_displayed( out_file ) != -1 )
        silent = 0;
#endif

output_aux_info:
    /*
        Output Aux Info
    */
    io.bFhTag = 0;

    if( (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) == 0 )
    {

        io.num_components = io.num_comp[io.iCurTautMode];

        /* AuxInfo: header and normalization type */
        intermediate_result = OutputAUXINFO_HeaderAndNormalization_type( pCG, out_file, strbuf,
                                                                            bINChIOutputOptions,
                                                                             &INCHI_basic_or_INCHI_reconnected,
                                                                             num_components2,
                                                                             &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;


repeat_INChI_Aux_output:
        /* AuxInfo: original atom numbers and symmetry numbers (constit. equivalence /E: )    */
        intermediate_result = OutputAUXINFO_OriginalNumbersAndEquivalenceClasses( pCG,
                                                                                out_file, strbuf,
                                                                                num_components2,
                                                                                &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;

        /* AuxInfo: tautomeric groups equivalence */
        intermediate_result = OutputAUXINFO_TautomericGroupsEquivalence( pCG,
                                                                            out_file, strbuf,
                                                                            &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;

        /* AuxInfo: stereo data */
        intermediate_result = OutputAUXINFO_Stereo( pCG, out_file, strbuf, &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;

repeat_INChI_Aux_Iso_output:
        /* AuxInfo: isotopic info */
        intermediate_result = OutputAUXINFO_IsotopicInfo( pCG, out_file, strbuf,
                                                            &INCHI_basic_or_INCHI_reconnected,
                                                            &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;


        /*
          At this point the INChI_Aux part of the output has been completed.
          If this INChI is tautomeric and non-tautomeric results exist,
          then we need to output non-tautomeric auxilialy data
          (same as above excluding tautomeric information).
          Currently, this is enabled for xml output only
        */

        if ( io.bOutType == OUT_TN && io.bTautomeric && io.bNonTautomeric &&
            /* Check whether the Fixed-H layer is empty */
            (*pSortPrintINChIFlags & ((INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS :
                                                             FLAG_SORT_PRINT_NO_NFIX_H_REC )) &&
            (*pSortPrintINChIFlags & ((INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS :
                                                             FLAG_SORT_PRINT_NO_IFIX_H_REC ))
              )
        {
            io.bNonTautomeric = 0; /* bNonTautIdentifierNotEmpty == 0 => no fixed H info 02-10-2995 */
        }

        if ( io.bOutType == OUT_TN && io.bTautomeric && io.bNonTautomeric )
        {
            /* add the second (non-tautomeric) output */
            io.bOutType     = OUT_NONTAUT;
            io.iCurTautMode = TAUT_NON;
            io.pINChISort    = io.pINChISortTautAndNonTaut[TAUT_NON];
            io.bSecondNonTautPass = 1;
            io.num_components = io.num_comp[io.iCurTautMode];
            io.bFhTag = AL_FIXH;
            inchi_strbuf_reset( strbuf ); /*pStr[io.tot_len=0] = '\0';*/

            /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */
            if ( !(*pSortPrintINChIFlags &
                    ((INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS :
                                            FLAG_SORT_PRINT_NO_NFIX_H_REC ))
               )
            {
                npass++;
                goto repeat_INChI_Aux_output;
            }
            else
            {
                npass++;
                goto repeat_INChI_Aux_Iso_output;
            }
        }
        else
        {
            if ( io.bOutType == OUT_NONTAUT && io.bOutputType == OUT_TN && io.bTautomeric && io.bNonTautomeric )
            {
                /* the second (non-taut) output has been done; restore variables */
                io.bOutType           = OUT_TN;
                io.iCurTautMode       = TAUT_YES;
                io.pINChISort         = io.pINChISortTautAndNonTaut[TAUT_YES];
                io.bSecondNonTautPass = 0;
                /* set correct num components for the reversibility info 02-10-2005 */
                io.num_components     = io.num_comp[io.iCurTautMode];
                io.bFhTag = 0;
            }
        }

        /*    Charges, radicals, unusual valences */
        intermediate_result = OutputAUXINFO_ChargesRadicalsAndUnusualValences( pCG,
                                                                                out_file, strbuf,
                                                                                &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;


        /* Output the original input structure -- quick fix */
        intermediate_result = OutputAUXINFO_ReversibilityInfo( pCG, out_file, strbuf,
                                                                 pOrigStruct, &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;

        /* Output polymeric Aux Info */
        intermediate_result = OutputAUXINFO_PolymerInfo( pCG,out_file, strbuf,
                                                            pOrigStruct, &io, pLF, pTAB );
        if ( intermediate_result != 0 )
            goto exit_function;

        /*
            Output INChI_Aux of the reconnected structure
        */

        bEmbeddedOutputCalled = 0;
        if ( bDisconnectedCoord && INCHI_basic_or_INCHI_reconnected == INCHI_BAS && (bINChIOutputOptions & INCHI_OUT_EMBED_REC) &&
             num_components2[INCHI_REC] && !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) )
        {
            int nRet;
            bEmbeddedOutputCalled = 1;
            inchi_ios_print( out_file, "%s", pLF );

            nRet = OutputINChI1( pCG, strbuf, pINChISortTautAndNonTaut2,
                                 INCHI_REC, NULL, 0 /*bDisconnectedCoord*/,
                                 bOutputType, INCHI_OUT_ONLY_AUX_INFO | bINChIOutputOptions,
                                 bAbcNumbers, bCtPredecessors, bNoStructLabels,
                                 num_components2, num_non_taut2, num_taut2,
                                 out_file, log_file, num_input_struct,
                                 szSdfLabel, szSdfValue, lSdfId,
                                 pSortPrintINChIFlags,save_opt_bits);

            if ( !nRet )
                goto exit_function; /* error */
        }

        /* Close INChI_Aux */
        if ( !bEmbeddedOutputCalled && !bPlainTextCommnts )
        {
            inchi_ios_print( out_file, "%s\n", (!num_components2[0] && !num_components2[1])? "//":"" );
            /* plain text comment earlier ended with LF */
        }

        /* in wINChI window, separate AuxInfo: from InChIKey: with blank line */
        inchi_ios_print( out_file, "%s",
                        (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":"");
    } /* end of output AuxInfo */

    ret = 1;


exit_function:


#ifdef TARGET_LIB_FOR_WINCHI
    /* @@@ If for any error we get here silent, display previously hidden output */
    if ( silent )
            if ( !inchi_ios_flush_not_displayed( out_file ) != -1  )
                silent = 0;
#endif

    if ( io.bOverflow )
        inchi_ios_print( out_file, "\nFATAL ERROR: Output buffer overflow\n");

    if ( intermediate_result )
    {
        ret = 0;
        inchi_ios_eprint(log_file, "InChI serialization error for structure #%d.%s%s%s%s\n",
                                    num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue));
    }

    return ret;
} /* OutputINChI1 */




/*
    szGetTag( ... )
*/
char *szGetTag( const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways )
{
    int i, j, bit, num, len;
    if ( 0 < nTag && nTag < 3 ) {
        /* no plain text comments: pick up the last tag */
        for ( i = 0, j = -1, bit = 1; i < MAX_TAG_NUM; i ++, bit <<= 1 ) {
            if ( bTag & bit ) {
                j = i;
            }
        }
        if ( j >= 0 ) {
            strcpy( szTag, nTag == 1? Tag[j].szXmlLabel : nTag == 2? Tag[j].szPlainLabel : "???" );
            if ( nTag != 2 ) {
                *bAlways = Tag[j].bAlwaysOutput;
            }
            return szTag;
        }
    } else
    if ( nTag == 3 ) {
        /* plain text with comments */
        szTag[0] = '{';
        szTag[1] = '\0';
        for ( i = 0, j = -1, bit = 1, num=0; i < MAX_TAG_NUM; i ++, bit <<= 1 ) {
            if ( bTag & bit ) {
                j = i;
                if ( num ++ ) {
                    strcat( szTag, ":" );
                }
                strcat( szTag, Tag[i].szPlainComment );
            }
        }
        if ( num ) {
            strcat( szTag, "}" );
            num = (int) strlen( Tag[j].szPlainLabel );
            len = (int) strlen( szTag );
            if ( len ) {
                memmove( szTag + num, szTag, len+1 );
                memcpy( szTag, Tag[j].szPlainLabel, num );
            } else {
                strcpy ( szTag, Tag[j].szPlainLabel );
            }
            *bAlways = Tag[j].bAlwaysOutput;
        } else {
            strcpy( szTag, "???" );
        }
        return szTag;
    }
    strcpy( szTag, "???" );
    return szTag;
}


/*
    str_LineStart( ... )

    if ind < 0 (common usage, plain text output)
        just resets buffer pStr (by placing '\0' into pStr[0] )
    *obsolete* if ind >=0 XML output    embeds val in between XML tags

*/
int str_LineStart( const char *tag,char *tag2, int val2,
                   INCHI_IOSTREAM_STRING *buf, int ind )
{
    if ( ind < 0 )
    {
        /* plain text output */
        inchi_strbuf_reset( buf );
    }
    /* else *obsolete* XML output, obsolete: NOT USED ANYMORE */

    return 0;
}


/*
    str_LineEnd( ... )

    First, checks if buffer overflow; then:
    if ind < 0 (common usage, plain text output)
        sets terminating '\0' in pStr,
        optionally adds leading tag (e.g., '/' or "/c" )
    *obsolete* if ind >=0 XML output

*/
int str_LineEnd( const char *tag, int *bOverflow,
                 INCHI_IOSTREAM_STRING *buf,
                 int ind, int bPlainTextTags )
{
    static const int  add_tag_len = sizeof(x_line_closing)-1 + sizeof(x_close_line)-1;
    int tag_len;

    /* check buffer overflow */
    if ( *bOverflow )
        return 1;

    if ( ind < 0 )
    {
        /* Plain text */
        /* insert plain text tag if:
           (a) pStr has non-zero length, or
           (b) ind < -1
        */
        if ( buf->pStr[0] || ind < -1 )
        {
            tag_len = bPlainTextTags ? (int) strlen( tag ) : 0;
            if ( tag_len > 0 )
            {
                int n_added = tag_len + 2 + 2;
                inchi_strbuf_update( buf, n_added );
                memmove( buf->pStr+tag_len, buf->pStr, buf->nUsedLength + 1 );
                                    /* NB: trailing 0 is also memmoved */
                memcpy( buf->pStr, tag, tag_len );
                /* to be sure...  */
                buf->nUsedLength = strlen( buf->pStr );
            }
        }
    }

    return 0;
}


/*
    CleanOrigCoord
*/
int CleanOrigCoord( MOL_COORD szCoord, int delim )
{
#define MIN_BOND_LENGTH   (1.0e-6)
    char szVal[LEN_COORD+1];
    MOL_COORD szBuf;
    char *q;
    int len, last, fst, dec_pnt, num_zer=0, len_buf = 0, e;
    int k, i;
    double coord;

    for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD )
    {
        memcpy( szVal, szCoord+k, LEN_COORD );
        szVal[LEN_COORD] = '\0';
        lrtrim(szVal, &len);
        coord = strtod(szVal, &q);
        if ( MIN_BOND_LENGTH > fabs(coord)  )
        {
            strcpy( szVal, "0" );
            len = 1;
            num_zer ++;
        }
        else
        {
            len = (int) (q - szVal);
            /* last = (last mantissa digit position + 1)  */
            if ( (q = strchr(szVal, 'e')) || (q = strchr(szVal, 'E')) ||
                 (q = strchr(szVal, 'd')) || (q = strchr(szVal, 'D')) ) {
                /* floating point */
                last = q - szVal;
                /* remove (+) and leading zeroes from the exponent */
                e = (int)strtol( szVal+last+1, &q, 10 ); /* exponent */
                if ( e ) {
                    /* new exp; update the length */
                    len = last+1+sprintf( szVal+last+1, "%d", e ); /* print exp without leading zeroes and '+' */
                } else {
                    /* exponent is zero */
                    len = last;
                }
            } else {
                last = len;
            }
            /* fst = (first mantissa digit); fst=1 if the sign is present, otherwise 0 */
            fst = (szVal[0]!='.' && !isdigit( UCINT szVal[0] ));
            /* dec_pnt = (decimal point position) or last */
            if ( q = strchr(szVal, '.') ) {
                dec_pnt = (int) (q - szVal);
            } else {
                dec_pnt = last;
            }
            last -= 1; /* last mantissa digit position */
            /* remove trailing zeroes in the range dec_pnt+1..last-1 */
            for ( i = last; dec_pnt < i &&  '0' == szVal[i]; i -- )
                ;
            if ( i == dec_pnt ) {
                i --; /* remove decimal point, too */
            }
            if ( i < last ) {
                memmove( szVal+i+1, szVal+last+1, len-last );
                len -= last-i;
            }
            /* remove leading zeroes */
            for ( i = fst; i < len && '0' == szVal[i]; i ++ )
                ;
            if ( i > fst ) {
                memmove( szVal + fst, szVal+i, len-fst );
                len -= i-fst;
            }
        }
        if ( len_buf )
            szBuf[len_buf++] = delim;
        memcpy( szBuf + len_buf, szVal, len ); /* does not copy zero termination*/
        len_buf += len;
    }
    /* zero termination */
    if ( len_buf < (int)sizeof(MOL_COORD) ) {
        memset( szBuf+len_buf, 0, sizeof(MOL_COORD) - len_buf);
    }
    memcpy( szCoord, szBuf, sizeof(MOL_COORD) );
    return num_zer;
#undef MIN_BOND_LENGTH
}


/*
    WriteOrigCoord
*/
int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i, char *szBuf, int buf_len )
{

    int j, num_zer, len, cur_len;
    char *p;
    MOL_COORD szCurCoord;
    cur_len = 0;
    for ( j = *i; j < num_inp_atoms; ) {
        memcpy( szCurCoord, szMolCoord[j], sizeof(szCurCoord));
        num_zer = CleanOrigCoord( szCurCoord, ',' );
        if ( NUM_COORD == num_zer ) {
            len = 0;
        } else {
            if ( p = (char *)memchr( szCurCoord, '\0', sizeof(szCurCoord)) ) {
                len = (int) (p - szCurCoord);
            } else {
                len = sizeof(szCurCoord);
            }
        }
        if ( len + cur_len + 1 < buf_len ) {
            if ( len ) {
                memcpy( szBuf + cur_len, szCurCoord, len * sizeof(szBuf[0]) );
            }
            szBuf[cur_len += len] = ';';
            cur_len ++;
            j ++;
        } else {
            break;
        }
    }
    szBuf[cur_len] = '\0';
    *i = j; /* next item */
    return cur_len;
}


/*
    WriteOrigAtoms

  number of atoms
  [c|n]              chiral/nonchiral

  Element
  #valence
  +/-[charge>1]
  .#rad  (#rad=1, 2, 3: singlet, doulet, triplet)
  [.]i#iso_mass
  [.]{o|e|u|?} atom parity = {1:2:3:4}
  [.]h[#of 1H>1]
  [.]d[#of 2H>1]
  [.]t[#of 3H>1]

  Note: . occurs only once and only if radical or 1-character element
*/
int WriteOrigAtoms( CANON_GLOBALS *pCG,
                    int num_inp_atoms, inp_ATOM *at, int *i,
                    char *szBuf, int buf_len, STRUCT_DATA *sd)
{
    int j, k, n, len, len0, cur_len, val, bonds_val, mw, parity, num_trans, is_ok, b_self;
    static const char szIsoH[] = "hdt";
    char szCurAtom[32];
    AT_NUMB nNeighOrder[MAXVAL], neigh;

    cur_len = 0;
    if ( 0 == *i ) {
        cur_len = sprintf( szBuf, "%d%s", num_inp_atoms,
                                  (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)?    "c" :
                                  (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL)? "n" : "" );
    }
    for ( j = *i; j < num_inp_atoms; ) {
        /* tetrahedral parity treatment */
        parity    = 0;
        num_trans = 0;
        if ( at[j].p_parity ) {
            /* verify neighbors */
            is_ok  = 1;
            b_self = 0;
            for ( n = 0, k = 0; n < MAX_NUM_STEREO_ATOM_NEIGH; n ++ ) {
                neigh = at[j].p_orig_at_num[n]-1;
                if ( is_in_the_list( at[j].neighbor, neigh, at[j].valence ) &&
                     at[neigh].orig_at_number  ==  at[j].p_orig_at_num[n] ) {
                    /* real neighbor */
                    nNeighOrder[k ++] = at[j].p_orig_at_num[n];
                } else
                if ( (int)neigh == j && at[neigh].orig_at_number  ==  at[j].p_orig_at_num[n] ) {
                    /* central atom is a neighbor */
                    num_trans = n; /* move this neighbor to 0 position permutation parity */
                    b_self ++;
                } else {
                    is_ok = 0;
                    break;
                }
            }
            if ( is_ok && b_self <= 1 && b_self + k == MAX_NUM_STEREO_ATOM_NEIGH ) {
                num_trans += insertions_sort( pCG, nNeighOrder, k, sizeof(nNeighOrder[0]), comp_AT_RANK );
                if ( ATOM_PARITY_WELL_DEF( at[j].p_parity ) ) {
                    parity = 2 - (num_trans + at[j].p_parity) % 2;
                } else
                if ( ATOM_PARITY_ILL_DEF( at[j].p_parity ) ) {
                    parity = at[j].p_parity;
                } else {
                    ; /* invalid atom parity */
                }
            } else {
                ;/* add error message here */
            }
        }

        len = len0 = (int) strlen( at[j].elname );
        memcpy( szCurAtom, at[j].elname, len );
        bonds_val = nBondsValenceInpAt( at+j, NULL, NULL );

        if ( (val=needed_unusual_el_valence( at[j].el_number, at[j].charge, at[j].radical,
                                 at[j].chem_bonds_valence, bonds_val, at[j].num_H, at[j].valence )) ||
             at[j].charge || at[j].radical || at[j].iso_atw_diff || NUM_ISO_H(at,j) || parity ) {
            /* valence */
            if ( val ) {
                len += sprintf( szCurAtom + len, "%d", val > 0? val : 0 );
            }
            /* charge */
            if ( val = at[j].charge ) {
                szCurAtom[len++] = val>0? '+' : '-';
                if ( (val = abs(val)) > 1 ) {
                    len += sprintf( szCurAtom + len, "%d", val );
                }
            }
            /* radical */
            if ( val = at[j].radical ) {
                len += sprintf(szCurAtom + len, ".%d", val);
            }
            /* isotopic shift */
            if ( val = at[j].iso_atw_diff ) {
                mw = get_atomic_mass_from_elnum( at[j].el_number );
                if ( val == 1 )
                    val = mw;
                else
                if ( val > 0 )
                    val = mw + val -1;
                else
                    val = mw + val;
                len += sprintf( szCurAtom + len, "%si%d", len == len0? ".":"", val );
            }
            /* parity */
            if ( parity ) {
                len += sprintf( szCurAtom + len, "%s%s", len == len0? ".":"",
                                parity == AB_PARITY_ODD?   "o" :
                                parity == AB_PARITY_EVEN?  "e" :
                                parity == AB_PARITY_UNKN?  "u" :
                                parity == AB_PARITY_UNDF?  "?" : "" );
            }
            /* implicit isotopic H */
            if ( NUM_ISO_H(at,j) ) {
                for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) {
                    if ( val = at[j].num_iso_H[k] ) {
                        len += sprintf( szCurAtom + len, "%s%c", len == len0? ".":"", szIsoH[k] );
                        if ( val > 1 ) {
                            len += sprintf(szCurAtom + len, "%d", val);
                        }
                    }
                }
            }
        }
        if ( len + cur_len < buf_len ) {
            memcpy( szBuf + cur_len, szCurAtom, len );
            cur_len += len;
            j ++;
        } else {
            break;
        }
        szBuf[cur_len] = '\0';
        *i = j;
    }
    return cur_len;
}


/*
    WriteOrigBonds( ... )

    Output bonds in ascending order of the neighboring atom original numbers

<bonds> bpA;bpAbpA... </bonds>

b = bond type:
=============
w = undefined stereo, double
s = single
d = double
t = triple
a = aromatic
p = up from the current atom to the neighbor
P = uP from the neighbor to the current atom
v = undefined stereo Either, single from the current atom to the neighbor
V = undefined stereo Either, single from the neighbor to the current atom
n = down from the current atom to the neighbor
N = dowN from the neighbor to the current atom

p = bond parity:
================
- = odd
+ = even
u = unknown
? = undefined
  = no parity (empty)


A = neighbor orig. atom number
===============
neighbor orig. atom number < number of the current atom
Number of the current atom: 2 until first ";", 3 until 2nd ";", etc.

*/
int WriteOrigBonds( CANON_GLOBALS *pCG,
                    int num_inp_atoms, inp_ATOM *at, int *i,
                    char *szBuf, int buf_len, STRUCT_DATA *sd)
{
    int j, k, k2, kk, len, cur_len, j2=0, bond_stereo, bond_char, bond_parity, bond_parityNM, num_trans;
    char szCurBonds[7*MAXVAL+2]; /* num_neigh*(1 byte bond type + 2 bytes for bond parity up to 4 digits per neighbor number) + at the end one ';' */
    AT_RANK nNeighOrder[MAXVAL];
    int  chain_len, pnxt_atom, pinxt2cur, pinxt_sb_parity_ord;
    int  chain_len2, pnxt_atom2, pinxt2cur2, pinxt_sb_parity_ord2, m1, m2;
    int  pcur_atom, picur2nxt, picur_sb_parity_ord;

    cur_len = 0;
    for ( j = *i; j < num_inp_atoms; ) {
        len = 0;
        if ( at[j].valence > 1 ) {
            for ( k = 0; k < at[j].valence; k ++ ) {
                nNeighOrder[k] = k;
            }
            pCG->m_pn_RankForSort = at[j].neighbor;
            num_trans = insertions_sort( pCG, nNeighOrder, at[j].valence, sizeof(nNeighOrder[0]), CompRank );
        } else {
            num_trans = 0;
            nNeighOrder[0] = 0;
        }
        for ( kk = 0; kk < at[j].valence; kk ++ ) {
            k = nNeighOrder[kk];
            j2 = at[j].neighbor[k];
            bond_parity = 0;
            bond_parityNM = 0;
            if ( j2 < j ) {
                bond_stereo = at[j].bond_stereo[k];
                switch( at[j].bond_type[k] ) {
                case BOND_TYPE_SINGLE:
                    switch( bond_stereo ) {
                    case  STEREO_SNGL_UP:
                        bond_char = 'p';
                        break;
                    case -STEREO_SNGL_UP:
                        bond_char = 'P';
                        break;
                    case  STEREO_SNGL_DOWN:
                        bond_char = 'n';
                        break;
                    case -STEREO_SNGL_DOWN:
                        bond_char = 'N';
                        break;
#if ( FIX_EITHER_STEREO_IN_AUX_INFO == 1 )
                    case  STEREO_SNGL_EITHER:
                        bond_char = 'v';
                        break;
                    case -STEREO_SNGL_EITHER:
                        bond_char = 'V';
                        break;
#else
                    case  STEREO_SNGL_EITHER:
                    case -STEREO_SNGL_EITHER:
                        bond_char = 'v';
                        break;
#endif
                    default:
                        bond_char = 's';
                        break;
                    }
                    break;
                case BOND_TYPE_DOUBLE:
                    switch( bond_stereo ) {
                    case  STEREO_DBLE_EITHER:
                    case -STEREO_DBLE_EITHER:
                        bond_char = 'w';
                        break;
                    default:
                        bond_char = 'd';
                        break;
                    }
                    break;
                case BOND_TYPE_TRIPLE:
                    bond_char = 't';
                    break;
                case BOND_TYPE_ALTERN:
                    bond_char = 'a';
                    break;
                default:
                    bond_char = 's';
                    break;
                }
                /* check for allene/cumulene */
                k2 = (int) (is_in_the_list( at[j2].neighbor, (AT_NUMB)j, at[j2].valence ) - at[j2].neighbor);
                chain_len = chain_len2 = 0;
                if ( at[j].sb_parity[0] ) {
                    for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[j].sb_parity[m1]; m1 ++ ) {
                        if ( k == at[j].sb_ord[m1] ) {
                            chain_len = get_opposite_sb_atom( at, j, k,
                                          &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord );
                            break;
                        }
                    }
                }
                if ( at[j2].sb_parity[0] ) {
                    for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[j2].sb_parity[m2]; m2 ++ ) {
                        if ( k2 == at[j2].sb_ord[m2] ) {
                            chain_len2 = get_opposite_sb_atom( at, j2, k2,
                                           &pnxt_atom2, &pinxt2cur2, &pinxt_sb_parity_ord2 );
                            break;
                        }
                    }
                }
                if ( chain_len == 1 && chain_len2 == 1 ||  /* regular stereobond */
                     chain_len  > 1 && j  > pnxt_atom ) {  /* j  is a cumulene endpoint */
                    int m;
                    pcur_atom = j;  /* pcur_atom > pnxt_atom */
                    picur2nxt = k;
                    picur_sb_parity_ord = -1;
                    for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) {
                        if ( at[pcur_atom].sb_ord[m] == k ) {
                            picur_sb_parity_ord = m;
                            break;
                        }
                    }
                    chain_len2 = 0;
                } else
                if ( chain_len2 > 1 && j2 > pnxt_atom2  ) { /* j2 is a cumulene endpoint */
                    int m;
                    pcur_atom = j2;
                    picur2nxt = k2;
                    pnxt_atom = pnxt_atom2;
                    pinxt2cur = pinxt2cur2;
                    pinxt_sb_parity_ord = pinxt_sb_parity_ord2;
                    picur_sb_parity_ord = -1;
                    for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) {
                        if ( at[pcur_atom].sb_ord[m] == k2 )
                            picur_sb_parity_ord = m;
                    }
                    chain_len  = chain_len2;
                    chain_len2 = 0;
                } else {
                    chain_len = chain_len2 = 0;
                }

                /*len += sprintf( szCurBonds + len, "%c%d", bond_char, val+1);*/
                if ( chain_len ) {
                    /* both atoms belong to a stereo bond */
                    int kc;
                    int p1, p2, p1NM, p2NM, neigh, neigh1, neigh2, bHasMetal, bWellDef;
                    int     bNeighSwitched1, bNeighSwitched2;

                    p1   = SB_PARITY_1(at[pcur_atom].sb_parity[picur_sb_parity_ord]);
                    p1NM = SB_PARITY_2(at[pcur_atom].sb_parity[picur_sb_parity_ord]);
                    p2   = SB_PARITY_1(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]);
                    p2NM = SB_PARITY_2(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]);

                    bWellDef  = ATOM_PARITY_WELL_DEF(p1)   && ATOM_PARITY_WELL_DEF(p2);
                    bHasMetal = ATOM_PARITY_WELL_DEF(p1NM) && ATOM_PARITY_WELL_DEF(p2NM);

                    bNeighSwitched1 = bNeighSwitched2 = 0;

                    if ( bWellDef || bHasMetal ) {

                        neigh1  = num_inp_atoms;
                        for ( kc = 0; kc < at[pcur_atom].valence; kc ++ ) {
                            if ( kc == picur2nxt )
                                continue;
                            neigh = at[pcur_atom].neighbor[kc];
                            if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) )
                                continue;
                            if ( neigh < neigh1 )
                                neigh1 = neigh;
                        }
                        if ( neigh1 < num_inp_atoms ) {
                             bNeighSwitched1 = (neigh1 != at[pcur_atom].neighbor[(int)at[pcur_atom].sn_ord[picur_sb_parity_ord]]);
                        } else {
                            AddErrorMessage(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor");
                            /*
                            sd->nStructReadError =  99;
                            sd->nErrorType = _IS_ERROR;
                            */
                        }

                        neigh2  = num_inp_atoms;
                        for ( kc = 0; kc < at[pnxt_atom].valence; kc ++ ) {
                            if ( kc == pinxt2cur )
                                continue;
                            neigh = at[pnxt_atom].neighbor[kc];
                            if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) )
                                continue;
                            if ( neigh < neigh2 )
                                neigh2 = neigh;
                        }
                        if ( neigh2 < num_inp_atoms ) {
                             bNeighSwitched2 = (neigh2 != at[pnxt_atom].neighbor[(int)at[pnxt_atom].sn_ord[pinxt_sb_parity_ord]]);
                        } else {
                            AddErrorMessage(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor");
                            /*
                            sd->nStructReadError =  99;
                            sd->nErrorType = _IS_ERROR;
                            */
                        }

                        if ( neigh1 < num_inp_atoms && neigh2 < num_inp_atoms ) {
                            if ( ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2) ) {
                                bond_parity = 2 - (p1 + p2 + bNeighSwitched1 + bNeighSwitched2) % 2;
                            } else {
                                bond_parity = inchi_min( p1, p2 );
                            }

                            if ( bHasMetal ) {
                                bond_parityNM = 2 - (p1NM + p2NM + bNeighSwitched1 + bNeighSwitched2) % 2;
                            } else
                            if ( p1NM && p2NM ) {
                                bond_parityNM = inchi_min( p1NM, p2NM );
                            }
                        }
                    } else {
                        if ( p1 && p2 ) {
                            bond_parity = inchi_min( p1, p2 );
                        }
                        if ( p1NM && p2NM ) {
                            bond_parityNM = inchi_min( p1NM, p2NM );
                        }
                        if ( bond_parityNM && !bond_parity ) {
                            bond_parity = AB_PARITY_UNDF;
                        }
                    }
                }
                len += sprintf( szCurBonds + len, "%c%s%s%d",

                                                  bond_char,

                                                  (bond_parity == AB_PARITY_ODD)?  "-" :
                                                  (bond_parity == AB_PARITY_EVEN)? "+" :
                                                  (bond_parity == AB_PARITY_UNKN)? "u" :
                                                  (bond_parity == AB_PARITY_UNDF)? "?" : "",

                                                  (bond_parityNM == AB_PARITY_ODD)?  "-" :
                                                  (bond_parityNM == AB_PARITY_EVEN)? "+" :
                                                  (bond_parityNM == AB_PARITY_UNKN)? "u" :
                                                  (bond_parityNM == AB_PARITY_UNDF)? "?" : "",

                                                  j2+1);
            }
        }
        if ( len + cur_len + 2 < buf_len ) {
            memcpy( szBuf + cur_len, szCurBonds, len );
            cur_len += len;
            szBuf[ cur_len ++ ] = ';';
            j ++;
        } else {
            break;
        }
    }
    szBuf[cur_len] = '\0';
    *i = num_inp_atoms>0? j : 0;
    return cur_len;
}


#define ORIG_STR_BUFLEN (7*MAXVAL+2)  /* > 7*MAXVAL+2 = 142 */


/*
    Fill out original input structure
*/
int OrigStruct_FillOut( CANON_GLOBALS *pCG,
                       ORIG_ATOM_DATA *orig_inp_data,
                       ORIG_STRUCT *pOrigStruct,
                       STRUCT_DATA *sd )
{
    char szBuf[ORIG_STR_BUFLEN];
    int  i, len, len_coord, len_atoms, len_bonds;

    pOrigStruct->polymer = NULL;
    pOrigStruct->v3000 = NULL;

    /* Coordinates */
    len_coord = i = 0;

    if (orig_inp_data->szCoord) {

        while ( len = WriteOrigCoord( orig_inp_data->num_inp_atoms,
                                      orig_inp_data->szCoord, &i, szBuf, sizeof(szBuf) )) {
            len_coord += len;
        }
        pOrigStruct->szCoord = (char*) inchi_malloc( (len_coord + 1)*sizeof(pOrigStruct->szCoord[0]) );
        i = 0;
        if ( pOrigStruct->szCoord &&
             len_coord == WriteOrigCoord( orig_inp_data->num_inp_atoms,
                                      orig_inp_data->szCoord, &i, pOrigStruct->szCoord, len_coord+1 ) &&
             i == orig_inp_data->num_inp_atoms ) {
            /* success */
            if ( orig_inp_data->szCoord ) {
                inchi_free( orig_inp_data->szCoord );
                orig_inp_data->szCoord = NULL;
            }
        } else {
            return -1;
        }
    }

    /* Atoms */
    len_atoms = i = 0;
    while ( len = WriteOrigAtoms( pCG, orig_inp_data->num_inp_atoms,
                                  orig_inp_data->at, &i, szBuf, sizeof(szBuf), sd))
    {
        len_atoms += len;
        if ( !orig_inp_data->num_inp_atoms )
            break;
    }
    pOrigStruct->szAtoms = (char*) inchi_malloc( (len_atoms + 1)*sizeof(pOrigStruct->szAtoms[0]) );
    i = 0;
    if ( pOrigStruct->szAtoms &&
         len_atoms == WriteOrigAtoms( pCG, orig_inp_data->num_inp_atoms,
                                  orig_inp_data->at, &i, pOrigStruct->szAtoms, len_atoms+1, sd ) &&
         i == orig_inp_data->num_inp_atoms ) {
        ; /* success */
    } else {
        return -1;
    }

    /* Bonds */
    len_bonds = 0;
    i = 1;
    while ( len = WriteOrigBonds( pCG, orig_inp_data->num_inp_atoms,
                                  orig_inp_data->at, &i, szBuf, sizeof(szBuf), NULL))
    {
        len_bonds += len;
        if ( !orig_inp_data->num_inp_atoms )
            break;
    }

    pOrigStruct->szBonds = (char*) inchi_malloc( (len_bonds + 2)*sizeof(pOrigStruct->szBonds[0]) );
    i = 1;

    if ( pOrigStruct->szBonds &&
         len_bonds == WriteOrigBonds( pCG, orig_inp_data->num_inp_atoms,
                                  orig_inp_data->at, &i, pOrigStruct->szBonds, len_bonds+2, sd ) &&
         i == orig_inp_data->num_inp_atoms )
    {
        ; /* success */
    }
    else
    {
        return -1;
    }
    pOrigStruct->num_atoms = orig_inp_data->num_inp_atoms;

    /* Extensions of v. 1.05 */
    if ( orig_inp_data->polymer !=NULL
         && orig_inp_data->polymer->n > 0
         && orig_inp_data->polymer->valid )
    {
        pOrigStruct->polymer = orig_inp_data->polymer;
                                /* pointer copy, do not free after use! */
    }
    if ( orig_inp_data->v3000 !=NULL)
    {
        pOrigStruct->v3000 = orig_inp_data->v3000;
                                /* pointer copy, do not free after use! */
    }

    return 0;
}


/*
    FreeOrigStruct( ... )
*/
void FreeOrigStruct(  ORIG_STRUCT *pOrigStruct)
{
    if ( pOrigStruct ) {
        if ( pOrigStruct->szAtoms )
            inchi_free( pOrigStruct->szAtoms );
        if ( pOrigStruct->szBonds )
            inchi_free( pOrigStruct->szBonds );
        if ( pOrigStruct->szCoord )
            inchi_free( pOrigStruct->szCoord );
        /* For

            OrigAtDataPolymer *polymer;
            OrigAtDataV3000    *v3000;

            we used shallow (pointer) copy of analogs from orig_inp_data, so do not free these here */

        /*memset( pOrigStruct, 0, sizeof(*pOrigStruct) );*/
        pOrigStruct->szAtoms = NULL;
        pOrigStruct->szBonds = NULL;
        pOrigStruct->szCoord = NULL;
    }
}


/*
    GetSaveOptLetters

        Get the two letters encoding the saved InChI creation options.

        The first one encodes RecMet/FixedH/SUU/SLUUD options.
        Each of options is a binary switch {ON,OFF}, so it totals to 2*2*2*2=16 values
        which are encoded by capital letters �A� through �P�.

        The second character encodes experimental (InChI 1 extension) options KET and 15T.
        Each of these options is a binary switch ON/OFF, so there are 2*2=4 combinations,
        currently encoded by �A� through �D�.
        Note that anything but 'A' here would indicate "extended" InChI 1 Also, there is a
        reservation for future needs: the 2nd memo char may accommodate two more ON/OFF
        binary options (at 26-base encoding).
*/
void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2)
{
const char a2p[]="ABCDEFGHIJKLMNOP";
    /* SaveOptBits layout: {unused|unused|Ket|15T|RecMet|FixedH|SUU|SLUUD} */
    *let1 = a2p [ (size_t) ( save_opt_bits & 0x0f ) ];
    *let2 = a2p [ (size_t) ( (save_opt_bits & 0x30) >> 4 ) ];
}


/*
    Set line separators dependent on requested output mode
*/
void set_line_separators( int bINChIOutputOptions, char **pLF, char **pTAB )
{
    int  bPlainTextCommnts    = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS);

    *pLF = bPlainTextCommnts? "\n" : "\0";

#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) )
    {
        int  bPlainText            = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS));
        int  bPlainTabbedOutput    = 0 != (bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT) &&
                                        bPlainText && !bPlainTextCommnts;

        *pTAB = bPlainTabbedOutput? "\t" : "\n";
    }
#else
    *pTAB = "\n";
#endif

    return;
}



/*
    Output structure (compound) header
*/
int Output_RecordInfo( INCHI_IOSTREAM *out_file, int num_input_struct,
                        int bNoStructLabels, const char *szSdfLabel,
                        const char *szSdfValue, long lSdfId,
                        char *pLF, char *pTAB )
{
    if ( bNoStructLabels )
        return 0;

#ifdef TARGET_LIB_FOR_WINCHI
    /*out_file->s.nUsedLength = 0;
    inchi_ios_reset( out_file );*/
#endif
    if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) )
    {
        inchi_ios_print_nodisplay( out_file, "%sStructure: %d", pLF, num_input_struct );
        inchi_ios_print_nodisplay( out_file, "%s", pTAB );
    }
    else
    {
        inchi_ios_print_nodisplay( out_file, "%sStructure: %d.%s%s%s%s", pLF, num_input_struct,
                                 SDF_LBL_VAL(szSdfLabel, szSdfValue) );
        if ( lSdfId )
        {
            (out_file->s.nUsedLength)--;
            inchi_ios_print_nodisplay( out_file, ":%ld", lSdfId );
        }
        inchi_ios_print_nodisplay( out_file, "%s", pTAB );
    }

    return 0;
}


/*
    Output InChI: InChI version and kind
*/
int OutputINCHI_VersionAndKind(    INCHI_IOSTREAM *out_file,
                                    INCHI_IOSTREAM_STRING *strbuf,
                                    int bINChIOutputOptions,
                                    int is_beta,
                                    char *pLF, char *pTAB )
{
    inchi_ios_print_nodisplay( out_file, "%s%s=%s", pLF, INCHI_NAME, pLF );

    inchi_strbuf_reset( strbuf );
    inchi_strbuf_printf( strbuf, "%s", x_curr_ver);

    /* - add 'Beta' flag if applicable */
    if ( is_beta )
        inchi_strbuf_printf( strbuf, "B");
    /* - add 'Standard' flag if applicable */
    else if ( bINChIOutputOptions & INCHI_OUT_STDINCHI )
        inchi_strbuf_printf( strbuf, "S");

    inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );

    return 0;
}


/*
    Output InChI: main layer - formula, connections and hydrogens (incl. tautomeric info == mobile H)
*/

int OutputINCHI_MainLayerFormula(    CANON_GLOBALS *pCG,
                                        INCHI_IOSTREAM *out_file,
                                        INCHI_IOSTREAM_STRING *strbuf,
                                        int num_components2[],
                                        int *INCHI_basic_or_INCHI_reconnected,
                                        OutputINCHI_CtlData *io,
                                        char *pLF, char *pTAB )
{

    /* constitution ( dot-disconnected Hill formulas: <formula> ) */

    if ( num_components2[0] || num_components2[1] )
    {
        szGetTag( IdentLbl, io->nTag,  io->bTag1 = *INCHI_basic_or_INCHI_reconnected==INCHI_REC? IL_REC_ : IL_FML_, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        io->tot_len = str_HillFormula( io->pINChISort, strbuf, &io->bOverflow, io->bOutType,
                                   io->num_components, io->bUseMulipliers);

        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, 1 ) )
            return 1;
        inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
    }
    return 0;
}


int OutputINCHI_MainLayerConnections( CANON_GLOBALS *pCG,
                                      INCHI_IOSTREAM *out_file,
                                      INCHI_IOSTREAM_STRING *strbuf,
                                      int num_components2[],
                                      int *INCHI_basic_or_INCHI_reconnected,
                                      OutputINCHI_CtlData *io,
                                      char *pLF, char *pTAB )
{
    /* connections ( semicolon/dot-disconnected connection tables ) */

    szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_CONN, io->szTag1, &io->bAlways );
    inchi_strbuf_reset( strbuf );
    io->tot_len  = 0;
    io->tot_len2 = str_Connections( pCG, io->pINChISort, strbuf, &io->bOverflow, io->bOutType,
                                    io->ATOM_MODE, io->num_components, io->bUseMulipliers);

    /* current version does not output empty (";;;;") connectivity */

    if ( io->tot_len != io->tot_len2 ) { /* 2004-06-30: never output empty connection table */
        io->tot_len = io->tot_len2;
        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -2, io->bPlainTextTags ) )
            return 1; /* pStr overfow */
        inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
    }

    return 0;
}


int OutputINCHI_MainLayerHydrogens(    CANON_GLOBALS *pCG,
                                    INCHI_IOSTREAM *out_file,
                                    INCHI_IOSTREAM_STRING *strbuf,
                                    int num_components2[],
                                    int *INCHI_basic_or_INCHI_reconnected,
                                    OutputINCHI_CtlData *io,
                                    char *pLF, char *pTAB )
{

    /* hydrogen atoms (do not output empty) */

    if ( INCHI_SEGM_FILL == INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_h_H_ATOMS] ) )
    {
        szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_ALLH, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        io->tot_len  = 0;
        io->tot_len2 = str_H_atoms( io->pINChISort, strbuf, &io->bOverflow, io->bOutType,
                                io->ATOM_MODE, io->TAUT_MODE,
                                io->num_components, io->bUseMulipliers);
        if ( io->tot_len != io->tot_len2 ) { /* 2004-06-21: never output empty */
            io->tot_len = io->tot_len2;
            if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -2, 1 ) )
                return 1;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }
    }

    return 0;
}


/*
    Output InChI: charge and  removed protons layers
*/
int OutputINCHI_ChargeAndRemovedAddedProtonsLayers( CANON_GLOBALS *pCG,
                                                 INCHI_IOSTREAM *out_file, INCHI_IOSTREAM_STRING *strbuf,
                                                 OutputINCHI_CtlData *io,
                                                 char *pLF, char *pTAB )
{

    /* charge  */

    io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_q_CHARGE] );
    if ( io->nSegmAction )
    {
        szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_CHRG | io->bFhTag, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        io->tot_len = 0;
        if ( INCHI_SEGM_FILL == io->nSegmAction )
        {
            io->tot_len = str_Charge2( io->pINChISort, io->pINChISort2,
                                   strbuf, &io->bOverflow, io->bOutType, io->num_components,
                                   io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers);
            io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
        }
        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
    }

    /* removed protons */

    if ( io->iCurTautMode == TAUT_YES && !io->bSecondNonTautPass )
    {

        io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_p_PROTONS] );
        if ( io->nSegmAction )
        {
            szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_PROT | io->bFhTag, io->szTag1, &io->bAlways );
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            inchi_strbuf_printf( strbuf, "%+d", io->nNumRemovedProtons );
            if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" );
        }
    }

    return 0;
}


/*
    Output InChI: stereo layer with sublayers
*/
int OutputINCHI_StereoLayer( CANON_GLOBALS *pCG,
                             INCHI_IOSTREAM *out_file, INCHI_IOSTREAM_STRING *strbuf,
                             OutputINCHI_CtlData *io,
                             char *pLF, char *pTAB )
{

    {
        int i;
        i = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] );
        i = i;
    }

    if ( INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) ||
         INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) ||
         INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) ||
         INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) )
    {

        /*  stereo */

        szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_STER | io->bFhTag, io->szTag1, &io->bAlways );

        /*  sp2 */

        /*if ( bStereoSp2[io->iCurTautMode]  )*/
        if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) )
        {
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_DBND, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            if ( INCHI_SEGM_FILL == io->nSegmAction )
            {
                io->tot_len = str_Sp2( io->pINChISort,
                                   io->pINChISort2,
                                   strbuf,
                                   &io->bOverflow,
                                   io->bOutType,
                                   io->TAUT_MODE,
                                   io->num_components,
                                   io->bSecondNonTautPass,
                                   io->bOmitRepetitions,
                                   io->bUseMulipliers);
                io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            }
            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* sp2 */
        }

        /*  sp3 */

        /*if ( bStereoSp3[io->iCurTautMode]  )*/
        if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) )
        {
            io->bRelRac     = io->bRelativeStereo[io->iCurTautMode] || io->bRacemicStereo[io->iCurTautMode];
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_SP3S, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            if ( INCHI_SEGM_FILL == io->nSegmAction )
            {
                io->tot_len = str_Sp3( io->pINChISort, io->pINChISort2,
                                   strbuf, &io->bOverflow, io->bOutType,
                                   io->TAUT_MODE, io->num_components, io->bRelRac,
                                   io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers);
                io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            }

            if (str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ))
                return 2;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* sp3 */
        }

        /* bStereoAbsInverted[io->iCurTautMode]  */

        /* if ( bStereoAbs[io->iCurTautMode]  ) */
        if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) )
        {
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_INVS, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0;
            if ( INCHI_SEGM_FILL == io->nSegmAction ) {
                io->tot_len = str_StereoAbsInv( io->pINChISort, strbuf,
                                            &io->bOverflow, io->bOutType, io->num_components);
                io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            }

            if (str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ))
                return 3;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print_nodisplay( out_file, "/" ); /* stereo-abs-inv */
        }

        /* stereo type */

        /*if ( io->bRacemicStereo[io->iCurTautMode] || io->bRelativeStereo[io->iCurTautMode] || bStereoAbs[io->iCurTautMode] )*/
        if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) )
        {
            const char *p_stereo = io->bRelativeStereo[io->iCurTautMode]? x_rel :
                                   io->bRacemicStereo[io->iCurTautMode] ? x_rac : x_abs;
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_TYPS, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0;
            if ( INCHI_SEGM_FILL == io->nSegmAction ) {
                (io->tot_len) += MakeDelim( p_stereo, strbuf, &io->bOverflow);
                io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            }
            if (str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ))
                return 1;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }
        if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" );  /* no abs, inv or racemic stereo */
    }
    else
    {
        if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */
    }

    return 0;
}


/*
    Output InChI: isotopic layer and sublayers
*/
int OutputINCHI_IsotopicLayer( CANON_GLOBALS *pCG,
                                 INCHI_IOSTREAM *out_file,
                                 INCHI_IOSTREAM_STRING *strbuf,
                                 int *INCHI_basic_or_INCHI_reconnected,
                                 OutputINCHI_CtlData *io,
                                 char *pLF, char *pTAB )
{

    if ( INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_i_IATOMS] ) )
    {
        /*  isotopic #1:  composition -- atoms -- do not output in xml if empty */
        szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_ISOT | io->bFhTag, io->szTag1, &io->bAlways );
        /* isotopic atoms without mobile H.
         * Fixed 2004-06-15: always output if not bXml. Note:
         * Previous condition if( bHasIsotopicAtoms[io->iCurTautMode] || bIsotopic && !bXml)
         * did not optput /i in case of only mobile isotopic H
         */
        if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_i_IATOMS] ) )
        {
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_ATMS, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            /*if ( bHasIsotopicAtoms[io->iCurTautMode] )*/
            if ( INCHI_SEGM_FILL == io->nSegmAction )
            {
                io->tot_len2 = str_IsoAtoms( io->pINChISort, io->pINChISort2,
                                         strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE,
                                         io->num_components, io->bAbcNumbers,
                                         io->bSecondNonTautPass, io->bOmitRepetitions,
                                         io->bUseMulipliers);
                io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            }
            else
            {
                io->tot_len2 = io->tot_len;
            }

            io->tot_len = io->tot_len2;
            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }

        /*  isotopic #1a:  composition -- exchangeable isotopic H (mobile H only) */
        /*if ( !io->bSecondNonTautPass && bHasIsoH )*/
        if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_h_H_ATOMS] ) )
        {
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_XCGA, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            (io->tot_len) += MakeIsoHString( io->num_iso_H, strbuf, io->TAUT_MODE, &io->bOverflow);
            io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                return 2;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
        }

        /***************************************************
         *
         *       Isotopic stereo
         *
         ***************************************************/

        /*if ( bIsotopicStereo[io->iCurTautMode] )*/
        if ( INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) ||
             INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) ||
             INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) ||
             INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) )
        {
            /*  stereo */
            szGetTag( IdentLbl, io->nTag,  io->bTag2 = io->bTag1 | IL_STER, io->szTag2, &io->bAlways );

            /************************
              isotopic #2:  sp2
             ************************/
            /*if ( bIsotopicStereoSp2[io->iCurTautMode]  )*/
            if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) )
            {
                szGetTag( IdentLbl, io->nTag,  io->bTag3 = io->bTag2 | IL_DBND, io->szTag3, &io->bAlways );
                inchi_strbuf_reset( strbuf );
                io->tot_len = 0;
                if ( INCHI_SEGM_FILL == io->nSegmAction )
                {
                    io->tot_len = str_IsoSp2( io->pINChISort, io->pINChISort2,
                                          strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE, io->num_components,
                                          io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers);
                    io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
                }
                if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                    return 3;
                inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
            }
            else
            {
                if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* iso sp2 */
            }

            /************************
              isotopic #3:  sp3
             ************************/
            /*if ( bIsotopicStereoSp3[io->iCurTautMode]  )*/
            if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) )
            {
                io->bRelRac = io->bIsotopicRelativeStereo[io->iCurTautMode] || io->bIsotopicRacemicStereo[io->iCurTautMode];

                szGetTag( IdentLbl, io->nTag,  io->bTag3 = io->bTag2 | IL_SP3S, io->szTag3, &io->bAlways );
                inchi_strbuf_reset( strbuf );
                io->tot_len = 0;
                if ( INCHI_SEGM_FILL == io->nSegmAction )
                {
                    io->tot_len = str_IsoSp3( io->pINChISort, io->pINChISort2,
                                          strbuf, &io->bOverflow, io->bOutType,
                                          io->TAUT_MODE, io->num_components, io->bRelRac,
                                          io->bSecondNonTautPass, io->bOmitRepetitions,
                                          io->bUseMulipliers);
                    io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
                }
                if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                    return 5;
                inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
            } else
            {
                if ( io->bPlainTextTags == 1 )
                    inchi_ios_print_nodisplay( out_file, "/" ); /* iso-sp3 */
            }

            /* isotopic #4: abs inverted */
            if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) )
            {
                szGetTag( IdentLbl, io->nTag,  io->bTag3 = io->bTag2 | IL_INVS, io->szTag3, &io->bAlways );
                inchi_strbuf_reset( strbuf );
                io->tot_len = 0;
                if ( INCHI_SEGM_FILL == io->nSegmAction )
                {
                    io->tot_len = str_IsoStereoAbsInv( io->pINChISort, strbuf,
                                                   &io->bOverflow, io->bOutType, io->num_components);
                    io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
                }
                if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                    return 5;
                inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
            }
            else
            {
                if ( io->bPlainTextTags == 1 )
                        inchi_ios_print_nodisplay( out_file, "/" );
            }

            /* isotopic #5: stereo type. Do not output if it has already been output in non-iso */
            if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) )
            {
                const char *p_stereo = io->bIsotopicRelativeStereo[io->iCurTautMode]? x_rel :
                                       io->bIsotopicRacemicStereo[io->iCurTautMode] ? x_rac : x_abs;
                szGetTag( IdentLbl, io->nTag,  io->bTag3 = io->bTag2 | IL_TYPS, io->szTag3, &io->bAlways );
                inchi_strbuf_reset( strbuf );
                io->tot_len = 0;
                if ( INCHI_SEGM_FILL == io->nSegmAction )
                {
                    io->tot_len += MakeDelim( p_stereo, strbuf, &io->bOverflow);
                    io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
                }
                if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                    return 6;
                inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
            }
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print_nodisplay( out_file, "/" );  /* no abs, inv or racemic stereo */
        }
        else
        {
            /* no isotopic stereo */
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print_nodisplay( out_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */
        }
    }
    else
    {
        if ( io->bPlainTextTags == 1 )
            inchi_ios_print_nodisplay( out_file, "///" ); /* isotopic composition, sp2, sp3 */
        if ( io->bPlainTextTags == 1 )
            inchi_ios_print_nodisplay( out_file, "//" );   /* inv or racemic stereo */
    }

#if ( CANON_FIXH_TRANS == 1 )
    if ( io->bOutType == OUT_NONTAUT && io->bOutputType == OUT_TN && io->bSecondNonTautPass &&
         INCHI_SEGM_FILL == INChI_SegmentAction( io->sDifSegs[DIFL_F][DIFS_o_TRANSP] ))
    {
        /* find and print non-tautomeric components transposition, if non-trivial */
        AT_NUMB *nTrans_n, *nTrans_s;

        if ( 0 < bin_AuxTautTrans(io->pINChISort, io->pINChISort2, &nTrans_n, &nTrans_s, io->bOutType,  io->num_components) )
        {
            /* a non-trivial transposition does exist; output start tag */
            szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_TRNS | io->bFhTag, io->szTag1, &io->bAlways );
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            /* print the transposition, cycle after cycle */
            io->tot_len = str_AuxTautTrans( pCG, nTrans_n, nTrans_s,
                                        strbuf, &io->bOverflow,
                                        io->TAUT_MODE, io->num_components);
            io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass;
            if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
                return 7;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
             /* detected transposition */
            (*io->pSortPrintINChIFlags) |=
                ( *INCHI_basic_or_INCHI_reconnected == INCHI_BAS )?  FLAG_SORT_PRINT_TRANSPOS_BAS :
                                                                        FLAG_SORT_PRINT_TRANSPOS_REC;
        }
        else
        {
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print_nodisplay( out_file, "/" );
        }
    }
#endif

    return 0;
}


/*
    Output InChI: FixedH layer and related sublayers
*/
int OutputINCHI_FixedHLayerWithSublayers( CANON_GLOBALS *pCG,
                                             INCHI_IOSTREAM *out_file,
                                             INCHI_IOSTREAM_STRING *strbuf,
                                             int *INCHI_basic_or_INCHI_reconnected,
                                             OutputINCHI_CtlData *io,
                                             char *pLF, char *pTAB,
                                             int *then_goto_repeat )
{

    *then_goto_repeat = 0;

    if ( io->bOutType == OUT_TN &&
         !(io->bSecondNonTautPass) &&
         io->bNonTautIsIdenticalToTaut &&
         io->bTautomeric &&
         io->bNonTautomeric )
    {
            /* Fixed-H layer is empty in the Identifier */
            (*io->pSortPrintINChIFlags) |=
                (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)?    FLAG_SORT_PRINT_NO_NFIX_H_BAS :
                                                                    FLAG_SORT_PRINT_NO_NFIX_H_REC;
            (*io->pSortPrintINChIFlags) |=
                (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)?    FLAG_SORT_PRINT_NO_IFIX_H_BAS :
                                                                    FLAG_SORT_PRINT_NO_IFIX_H_REC;
    }

    if ( io->bOutType == OUT_TN &&
         !io->bNonTautIsIdenticalToTaut && /* added 2004-10-04 Fix16 */
#ifdef OLD_ITEM_DISCOVERY
         io->bTautomeric &&
         io->bNonTautomeric &&
#endif
         INChI_SegmentAction( io->sDifSegs[DIFL_F][DIFS_f_FORMULA] )
                                    /* special case: removed isolated H(+): */
                                    /* || io->iCurTautMode == TAUT_YES && num_comp[TAUT_YES] < num_comp[TAUT_NON] &&
                                        0 < num_comp[TAUT_NON]*/
       )

    {
        /* add the second (non-tautomeric) output */
        io->bOutType     = OUT_NONTAUT;    /* pick up only non-tautomeric representation of tautomeric */
        io->iCurTautMode = TAUT_NON;
        io->pINChISort    = io->pINChISortTautAndNonTaut[TAUT_NON];
        io->bSecondNonTautPass = 1;
        io->nCurINChISegment   = DIFL_F;
        io->num_components = io->num_comp[io->iCurTautMode]; /* number of components could change due to removal of isolated H(+) from tautomeric */
        io->bFhTag = IL_FIXH;
        szGetTag( IdentLbl, io->nTag,  io->bTag1 = io->bFhTag, io->szTag1, &io->bAlways );
        /***** constitution non-taut: dot-disconnected Hill formulas: <formula> -- only if different */
        szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_FMLF | io->bFhTag, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf ); io->tot_len = 0;
        io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_f_FORMULA] );
        if ( INCHI_SEGM_FILL == io->nSegmAction )
        {
            io->tot_len2 = str_HillFormula2( io->pINChISort, io->pINChISort2,
                                         strbuf, &io->bOverflow, io->bOutType,
                                         io->num_components, io->bUseMulipliers);
            io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
        }
        else
        {
            io->tot_len2 = io->tot_len;
        }
        io->tot_len = io->tot_len2;
        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );

        io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_h_H_ATOMS] );

        if ( INCHI_SEGM_FILL == io->nSegmAction )
        {
            szGetTag( IdentLbl, io->nTag,  io->bTag1 = IL_HFIX | io->bFhTag, io->szTag1, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0; /* open H-fixed */
            /* output the second non-tautomeric item: fixed H -- do not output in xml if empty */
            io->tot_len2 = str_FixedH_atoms( io->pINChISort, strbuf,
                                            &io->bOverflow, io->bOutType, io->ATOM_MODE,
                                            io->num_components, io->bUseMulipliers);
            io->tot_len = io->tot_len2;
            if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) )
                return 2;
            inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );
            io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass;
        }
        *then_goto_repeat = 1;
        return 0;
    }
    else
    {
        if ( io->bOutType == OUT_NONTAUT && io->bOutputType == OUT_TN && io->bSecondNonTautPass /* && io->bTautomeric && io->bNonTautomeric*/ )
        {
            /* the second (non-taut) output has been done; restore variables */
            io->bOutType           = OUT_TN;
            io->iCurTautMode       = TAUT_YES;
            io->pINChISort         = io->pINChISortTautAndNonTaut[TAUT_YES];
            io->bSecondNonTautPass = 0;
            io->num_components     = io->num_comp[io->iCurTautMode];
            if ( !io->bNonTautNonIsoIdentifierNotEmpty )
            {
                /* Fixed-H layer is empty in the Identifier */
                (*io->pSortPrintINChIFlags) |= (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS :
                                                            FLAG_SORT_PRINT_NO_NFIX_H_REC;
            }
            if ( !io->bNonTautIsoIdentifierNotEmpty )
            {
                /* Fixed-H layer is empty in the Identifier */
                (*io->pSortPrintINChIFlags) |= (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS :
                                                                FLAG_SORT_PRINT_NO_IFIX_H_REC;
            }
            io->bFhTag = 0;
        }
    }

    return 0;
}


/*
    Output InChI: polymer layer
*/
static int OutputINCHI_PolymerLayer( CANON_GLOBALS *pCG,
                                     INCHI_IOSTREAM *out_file,
                                     INCHI_IOSTREAM_STRING *strbuf,
                                     int *INCHI_basic_or_INCHI_reconnected,
                                     ORIG_STRUCT *pOrigStruct,
                                     OutputINCHI_CtlData *io,
                                     char *pLF, char *pTAB )
{
    int i, j, b, k, err = 0;
    int tmp, a1=0, a2=0, a3=0, a4=0, n_used_stars=0, curr_star_num;
    int nunits2 = 0;
    int *cano_nums = NULL, *compnt_nums = NULL, *unum = NULL, *old_stars=NULL;
    OrigAtDataPolymerUnit *u=NULL;
    OrigAtDataPolymerUnit **units2=NULL;
    OrigAtDataPolymer *p = NULL;

    int is_inchi2inchi = 0;

    if ( pOrigStruct && pOrigStruct->polymer )
        p = pOrigStruct->polymer;

    if ( NULL== p )
        return 0;

    is_inchi2inchi =  !pOrigStruct->szAtoms && !pOrigStruct->szBonds && !pOrigStruct->szCoord ;
    if ( is_inchi2inchi )
    {
        err = NOT_YET_I2I_FOR_POLYMERS;
        goto exitf;
    }


    OrigAtDataPolymer_DebugTrace (p );

    /* Get canonical numbers and numbers-of-components for each original atom */
    cano_nums    = (int *) inchi_calloc( pOrigStruct->num_atoms + 1, sizeof(int)  );
    if ( NULL    == cano_nums )
    {
        err = 1;
        goto exitf;
    }
    compnt_nums    = (int *) inchi_calloc( pOrigStruct->num_atoms + 1, sizeof(int)  );
    if ( NULL    == compnt_nums )
    {
        err = 2;
        goto exitf;
    }
    err = get_canonical_atom_numbers_and_component_numbers( pCG, strbuf, io,
                                                            pOrigStruct->num_atoms,
                                                            cano_nums, compnt_nums );
    if ( err != 0 )
    {
        err = 3;
        goto exitf;
    }


    /* Make a working copy of polymer units data */

    units2 = ( OrigAtDataPolymerUnit ** ) inchi_calloc( p->n, sizeof(OrigAtDataPolymerUnit*) );
        /*    units2    is a copy of original polymer units (p->units) with atomic numbers
                    changed to curr canonical ones; atoms in alists sorted; atoms in blists
                    and blists themselves sorted */
    if ( NULL==units2 )
    {
        err = 3;
        goto exitf;
    }
    memset( units2, 0, sizeof( *units2 ) );

    old_stars    = (int *) inchi_calloc( pOrigStruct->polymer->n_star_atoms, sizeof(int)  );
    if ( NULL == old_stars)
    {
        err = 3;
        goto exitf;
    }
    for (i=0; i<pOrigStruct->polymer->n_star_atoms; i++)
        old_stars[i] = pOrigStruct->polymer->star_atoms[i];


    for (i=0; i<p->n; i++)
    {
        units2[i] = OrigAtDataPolymerUnit_CreateCopy( p->units[i] );
        if ( NULL == units2[i] )
        {
            err = 4;
            goto exitf;
        }
        nunits2 = i + 1;
    }

    unum = (int *) inchi_calloc( p->n, sizeof(int) );
        /*    unum contains numbers of units (0..p->n) as they go
            when sorted by alist's in lexicographic orders      */
    if ( NULL == unum )
    {
        err = 4;
        goto exitf;
    }


    err = OrigAtDataPolymer_PrepareWorkingSet( p, cano_nums, compnt_nums, units2, unum );

    if ( err != 0 )
    {
        err = 5;
        goto exitf;
    }


    /* Prepare polymer substring */

    /* Mark layer beginning */
    inchi_strbuf_printf( strbuf, "%s", "/z");

    /* Print polymer units data */
    n_used_stars = 0;
    for (i=0; i<p->n; i++)
    {
        /* For each unit u ... */
        u = units2[ unum[i] ];

        /* print kinds of unit */
        inchi_strbuf_printf( strbuf, "%-d%-d%-d-", u->type, u->subtype, u->conn );

        /* print unit atoms */
        print_sequence_of_nums_compressing_ranges( u->na, u->alist, strbuf );

        /* Print unit-to-outside bonds or just outside-star-atoms (if phase-shiftable bonding) */
        if ( u->nb > 2 )
        {
            /* not supported yet, too many bonds in SBL */
            err = 12;
            goto exitf;
        }
        /* Print bonds from unit to otside */
        if ( u->nb == 2 && ( !u->closeable || !u->already_closed ) )
        {
            a1 = u->blist[0]; a2 = u->blist[1]; a3 = u->blist[2]; a4 = u->blist[3];
            if ( is_in_the_ilist( u->alist, a1, u->na ) )    { tmp = a2; a2 = a1; a1 = tmp; }
            if ( is_in_the_ilist( u->alist, a3, u->na ) )    { tmp = a4; a4 = a3; a3 = tmp; }
            if ( a1 < a3 )
                inchi_strbuf_printf( strbuf, "(%-d-%-d,%-d-%-d)",    a1, a2, a3, a4 );
            else
                inchi_strbuf_printf( strbuf, "(%-d-%-d,%-d-%-d)",    a3, a4, a1, a2 );
        }
        else if ( u->nb <= 2 && ( u->closeable || u->npsbonds > 0 ) )
        {
            /* indicate phase shifted */

            /*    Get actual star atoms numbers from all-units pool
                NB: ordered according to already established in units2/unum        */
            if ( u->star1 > 0 || u->star2 > 0 )
            {
                int n_expl_H = 0, pos = 0;
                char *sza = pOrigStruct->szAtoms;
                while ( sza[pos] )
                {
                    if ( sza[pos]=='H' )
                    {
                        if ( isupper(UCINT sza[pos+1]) || !sza[pos+1] )        /* if ( next_c is Uppercase or NUL ) */
                            n_expl_H++;
                        if ( !sza[pos+1] )
                            break;
                    }
                    pos++;
                }

                if ( u->star1 > 0 )
                {
                    curr_star_num = pOrigStruct->num_atoms - n_expl_H - p->n_star_atoms + n_used_stars + 1;
                    if ( curr_star_num > pOrigStruct->num_atoms )
                        { err = 11; goto exitf; }
                    a1 = curr_star_num;
                    n_used_stars++;
                }
                if ( u->star2 > 0 )
                {
                    curr_star_num = pOrigStruct->num_atoms - n_expl_H - p->n_star_atoms + n_used_stars + 1;
                    if ( curr_star_num > pOrigStruct->num_atoms )
                        { err = 11; goto exitf; }
                    a2 = curr_star_num;
                    n_used_stars++;
                }
            }

            /* if ( a1 > a2 )    { tmp = a2; a2 = a1; a1 = tmp; } */
            inchi_strbuf_printf( strbuf, "(%-d,%-d-", a1,a2 );

            if ( u->closeable == CLOSING_SRU_DIRADICAL )
            {
                inchi_strbuf_printf( strbuf, "%-d)", u->star_partner1 );
            }
            else if ( u->closeable == CLOSING_SRU_HIGHER_ORDER_BOND )
            {
                /*a3 = u->psbonds[0][0]; a4 = u->psbonds[0][1]; */
                a3 = u->star_partner1;
                a4 = u->star_partner2;
                if ( a3 > a4 )
                    { tmp = a4; a4 = a3; a3 = tmp; }
                inchi_strbuf_printf( strbuf, "%-d.%-d)", a3, a4 );
            }
            else if  ( u->closeable == CLOSING_SRU_RING )
            {
                if ( u->npsbonds == 0 )
                {
                    /* last resort */
                    a3 = u->star_partner1;
                    a4 = u->star_partner2;
                    if ( a3 > a4 )
                        { tmp = a4; a4 = a3; a3 = tmp; }
                    inchi_strbuf_printf( strbuf, "%-d,%-d)", a3, a4 );
                }
                else
                {
                    /* Sort all psbonds in min-at-number order */
                    for (b=1; b<u->npsbonds; b++)
                    {
                        int *tmp = u->psbonds[b];
                        j = b - 1;
                        while ( j >= 0 &&    psbond_min_num_compare( u->psbonds[j], tmp ) > 0  )
                        {
                            u->psbonds[j+1] = u->psbonds[j];
                            j--;
                        }
                        u->psbonds[j+1] = tmp;
                    }
                    for (k=0; k<u->npsbonds;k++)
                    {
                        a3 = u->psbonds[k][0]; a4 = u->psbonds[k][1];
                        /*if ( a3 > a4 )    { tmp = a4; a4 = a3; a3 = tmp; }*/
                        inchi_strbuf_printf( strbuf, "%-d,%-d%-c", a3, a4, k==u->npsbonds-1?')':',' );
                    }
                }
            }
            }

        if ( i < p->n-1 )    inchi_strbuf_printf( strbuf, ";" );
    }




    inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF );

exitf:
    if ( NULL != cano_nums )    inchi_free( cano_nums );
    if ( NULL != compnt_nums )    inchi_free( compnt_nums );
    if ( unum )                    inchi_free( unum );
    if ( NULL != units2 )
    {
        for (i=0; i < nunits2; i++)
            OrigAtDataPolymerUnit_Free( units2[i] );
        inchi_free( units2 );
    }
    if (old_stars )
    {
        for (i=0; i<pOrigStruct->polymer->n_star_atoms; i++)
            pOrigStruct->polymer->star_atoms[i] = old_stars[i];
        inchi_free(old_stars );
    }

    return err;
}


/*
    Output AuxInfo: header and normalization type
*/
int OutputAUXINFO_HeaderAndNormalization_type( CANON_GLOBALS *pCG,
                                                  INCHI_IOSTREAM *out_file,
                                                  INCHI_IOSTREAM_STRING *strbuf,
                                                  int bINChIOutputOptions,
                                                  int *INCHI_basic_or_INCHI_reconnected,
                                                  int num_components2[],
                                                  OutputINCHI_CtlData *io,
                                                  char *pLF, char *pTAB )
{
    /* AuxInfo header  */
    if ( *INCHI_basic_or_INCHI_reconnected==INCHI_BAS )
    {
        inchi_strbuf_printf( strbuf, "AuxInfo=" ); /* in wINChI window, separate INChI: from AuxInfo: with blank line */
        inchi_ios_print( out_file, "%s%s%s",
                                  /* blank line before AuxInfo in winchi window unless it is an annotation */
                                  (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":"",
                                  strbuf->pStr, pLF);
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_VERS, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf ); io->tot_len = 0;
        inchi_strbuf_printf( strbuf, "%s", x_curr_ver);
        /* avoid leading slash in plain output */
        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
    }
    else
    {
        if ( *INCHI_basic_or_INCHI_reconnected == INCHI_REC )
        {
            szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_REC_, io->szTag1, &io->bAlways );
            inchi_ios_print( out_file, "%s%s", io->szTag1, pLF );
        }
    }

    /* AuxInfo normalization type */
    if ( num_components2[0] || num_components2[1] )
    {
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_NORM, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf ); io->tot_len = 0;
        inchi_strbuf_printf( strbuf, "%d", (io->bTautomeric && io->bTautomericOutputAllowed)? io->bTautomeric : 0);
        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
    }

    return 0;
}


/*
    Output AuxInfo: original atom numbers and symmetry numbers (constit. equivalence /E: )
*/
int OutputAUXINFO_OriginalNumbersAndEquivalenceClasses( CANON_GLOBALS *pCG,
                                                            INCHI_IOSTREAM *out_file,
                                                            INCHI_IOSTREAM_STRING *strbuf,
                                                            int num_components2[],
                                                            OutputINCHI_CtlData *io,
                                                            char *pLF, char *pTAB )
{
    /* Original atom numbers in order of canonical numbers */
    if ( num_components2[0] || num_components2[1] )
    {
        szGetTag( AuxLbl, io->nTag,
                 io->bTag1 = (io->bSecondNonTautPass? AL_FIXN : AL_ANBR) | io->bFhTag, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        io->tot_len = 0;
        /* Original numbering output */
        io->tot_len = str_AuxNumb(  pCG, io->pINChISort, io->pINChISort2,
                                   strbuf, &io->bOverflow,
                                   io->bOutType, io->TAUT_MODE, io->num_components,
                                   io->bSecondNonTautPass, io->bOmitRepetitions);

        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
    }

    /*
        Symmetry numbers (constit. equivalence)    /E:
    */
    if ( io->bAtomEqu[io->iCurTautMode] )
    {
        /*  aux equ atoms */
        /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */
        /* 2. Compare to the previous component if (1) failed to find equivalence */
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_AEQU | io->bFhTag, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        io->tot_len = 0;
        io->tot_len = str_AuxEqu( io->pINChISort, io->pINChISort2,
                              strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE,
                              io->num_components, io->bSecondNonTautPass,
                              io->bOmitRepetitions, io->bUseMulipliers);

        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
    }
    else
    {
        if ( io->bPlainTextTags == 1 )
            inchi_ios_print( out_file, "/" );
    }

    return 0;
}


/*
    Output AuxInfo: tautomeric groups equivalence
*/
int OutputAUXINFO_TautomericGroupsEquivalence( CANON_GLOBALS *pCG,
                                                  INCHI_IOSTREAM *out_file,
                                                  INCHI_IOSTREAM_STRING *strbuf,
                                                  OutputINCHI_CtlData *io,
                                                  char *pLF, char *pTAB )
{
    if ( io->bTautomericOutputAllowed && io->bTautomeric && io->bTautEqu[io->iCurTautMode] && !io->bSecondNonTautPass )
    {
        /*-- Tautomeric groups constitutional equivalence */

        /*-- aux tgroup equ */
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_GEQU | io->bFhTag, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf ); io->tot_len = 0;
        io->tot_len = str_AuxTgroupEqu( io->pINChISort,
                                    strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE,
                                    io->num_components, io->bUseMulipliers);
        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print( out_file, "%s", strbuf->pStr );
    }
    else
    {
        if ( io->bTautomericOutputAllowed && io->bTautomeric )
        {
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print( out_file, "/" );
        }
    }

    return 0;
}


/*
    Output AuxInfo: stereo info
*/
int OutputAUXINFO_Stereo( CANON_GLOBALS *pCG,
                           INCHI_IOSTREAM *out_file,
                           INCHI_IOSTREAM_STRING *strbuf,
                           OutputINCHI_CtlData *io,
                           char *pLF, char *pTAB )
{
    /*--    Inverted stereo -- sp3 only + canonical numbering
    */
    if ( io->bInvStereo[io->iCurTautMode] )
    {
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_STER | io->bFhTag, io->szTag1, &io->bAlways );
        /*-- inverted sp3 start tag */
        szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_SP3I, io->szTag2, &io->bAlways );
        inchi_strbuf_reset( strbuf ); io->tot_len = 0;
        io->tot_len = str_AuxInvSp3( io->pINChISort, io->pINChISort2, strbuf,
                                 &io->bOverflow, io->bOutType, io->TAUT_MODE, io->num_components,
                                 io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers);
        if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;
        inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );

        /*-- inverted sp3  canonical numbering */
        if ( io->bInvStereoOrigNumb[io->iCurTautMode] )
        {
            szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_SP3N, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0;

            io->tot_len = str_AuxInvSp3Numb( pCG, io->pINChISort, io->pINChISort2,
                                         strbuf, &io->bOverflow, io->bOutType,
                                         io->TAUT_MODE, io->num_components,
                                         io->bSecondNonTautPass, io->bOmitRepetitions);

            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bPlainTextTags == 1 ) inchi_ios_print( out_file, "/" );
        }
    }
    else
    {
        if ( io->bPlainTextTags == 1 )
            inchi_ios_print( out_file, "//" );
        /* Inverted stereo -- sp3 only + canonical numbering */
    }

    return 0;
}


/*
    Output AuxInfo: isotopic info
*/
int OutputAUXINFO_IsotopicInfo( CANON_GLOBALS *pCG,
                                  INCHI_IOSTREAM *out_file,
                                  INCHI_IOSTREAM_STRING *strbuf,
                                  int *INCHI_basic_or_INCHI_reconnected,
                                  OutputINCHI_CtlData *io,
                                  char *pLF, char *pTAB )
{
int i;

    /* if InChI Fixed-H isotopic is empty, then do not output corresponding AuxInfo */

    i =  io->bSecondNonTautPass &&
         (*io->pSortPrintINChIFlags & ((*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS :
                                                                                  FLAG_SORT_PRINT_NO_IFIX_H_REC ));

    if ( io->bIsotopic && !i &&
                      (io->bIsotopicOrigNumb[io->iCurTautMode] ||
                       io->bIsotopicAtomEqu[io->iCurTautMode] ||
                       io->bTautomericOutputAllowed && io->bTautomeric && io->bIsotopicTautEqu[io->iCurTautMode] ||
                       io->bInvIsotopicStereo[io->iCurTautMode]
                       && ( io->bIgn_UU_Sp3_Iso[io->iCurTautMode] || io->bIgn_UU_Sp2_Iso[io->iCurTautMode] ) ) )
    {
        /*-- isotopic aux info header */
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_ISOT | io->bFhTag, io->szTag1, &io->bAlways );
        inchi_strbuf_reset( strbuf ); /* pStr[io->tot_len = 0] = '\0'; */
        /*-- Original atom numbers in order of isotopic canonical numbers */
        szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_ISON, io->szTag2, &io->bAlways );
        if ( io->bIsotopicOrigNumb[io->iCurTautMode] )
        {
            inchi_strbuf_reset( strbuf );
            io->tot_len = 0;
            io->tot_len = str_AuxIsoNumb( pCG, io->pINChISort, io->pINChISort2,
                                      strbuf, &io->bOverflow, io->bOutType,
                                      io->TAUT_MODE, io->num_components,
                                      io->bSecondNonTautPass, io->bOmitRepetitions);
            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            /*if ( io->bPlainTextTags == 1 ) inchi_ios_print( out_file, "/" );*/
            inchi_ios_print( out_file, "%s%s", io->szTag2, pLF ); /* mark isotopic output */
        }

        /*-- Isotopic symmetry */
        if ( io->bIsotopicAtomEqu[io->iCurTautMode] )
        {
            /*-- atoms */
            szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_AEQU, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0;
            io->tot_len = str_AuxIsoEqu( io->pINChISort, io->pINChISort2,
                                     strbuf,
                                     &io->bOverflow, io->bOutType, io->TAUT_MODE, io->num_components,
                                     io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers);
            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -2/*was -1: Fix15*/, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print( out_file, "/" );
        }

        /*-- Tautomeric groups, isotopic */
        if ( io->bTautomericOutputAllowed && io->bTautomeric && io->bIsotopicTautEqu[io->iCurTautMode] )
        {
            /*-- Isotopic tautomeric groups equivalence */
            szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_GEQU, io->szTag2, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0;
            io->tot_len = str_AuxIsoTgroupEqu( io->pINChISort,
                                           strbuf, &io->bOverflow,
                                           io->bOutType, io->TAUT_MODE, io->num_components,
                                           io->bOmitRepetitions, io->bUseMulipliers);
            if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -2/*was -1: Fix15*/, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
        }
        else
        {
            if ( io->bTautomericOutputAllowed && io->bTautomeric )
            {
                if ( io->bPlainTextTags == 1 )
                    inchi_ios_print( out_file, "/" );
            }
        }
        /*-- Isotopic inverted stereo */
        if ( io->bInvIsotopicStereo[io->iCurTautMode] )
        {
            szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_STER, io->szTag2, &io->bAlways );
            /*-- inverted isotopic sp3 start tag */
            szGetTag( AuxLbl, io->nTag,  io->bTag3 = io->bTag2 | AL_SP3I, io->szTag3, &io->bAlways );
            inchi_strbuf_reset( strbuf ); io->tot_len = 0;
            io->tot_len = str_AuxInvIsoSp3( io->pINChISort, io->pINChISort2,
                                        strbuf, &io->bOverflow,
                                        io->bOutType, io->TAUT_MODE, io->num_components,
                                        io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers);
            if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
                return 1;
            inchi_ios_print( out_file, "%s", strbuf->pStr );
            /*-- inverted isotopic sp3  canonical numbering */
            if ( io->bInvIsotopicStereoOrigNumb[io->iCurTautMode] )
            {
                szGetTag( AuxLbl, io->nTag,  io->bTag3 = io->bTag2 | AL_SP3N, io->szTag3, &io->bAlways );
                inchi_strbuf_reset( strbuf ); io->tot_len = 0;
                io->tot_len = str_AuxInvIsoSp3Numb( pCG, io->pINChISort, io->pINChISort2,
                                                strbuf, &io->bOverflow,
                                                io->bOutType, io->TAUT_MODE,
                                                io->num_components,
                                                io->bSecondNonTautPass,
                                                io->bOmitRepetitions);

                if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
                    return 1;
                inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
            }
            else
            {
                if ( io->bPlainTextTags == 1 )
                    inchi_ios_print( out_file, "/" );
            }
        }
        else
        {
            if ( io->bPlainTextTags == 1 )
                inchi_ios_print( out_file, "//" );
        }
        /*-- totally omitted undefined/unknown isotopic stereo */
    } /* Aux info isotopic */

return 0;
}


/*
    Output AuxInfo:
        charges, radicals, unusual valences
*/
int OutputAUXINFO_ChargesRadicalsAndUnusualValences( CANON_GLOBALS *pCG,
                                                      INCHI_IOSTREAM *out_file,
                                                      INCHI_IOSTREAM_STRING *strbuf,
                                                      OutputINCHI_CtlData *io,
                                                      char *pLF, char *pTAB )
{
    if ( !io->bSecondNonTautPass && io->bChargesRadVal[io->iCurTautMode] )
    {
        /*  aux equ atoms */
        /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */
        /* 2. Compare to the previous component if (1) failed to find equivalence */
        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_CRV_ | io->bFhTag, io->szTag1, &io->bAlways );

        inchi_strbuf_reset( strbuf );
        io->tot_len = 0;

        io->tot_len = str_AuxChargeRadVal( io->pINChISort, strbuf,
                                           &io->bOverflow, io->bOutType, io->TAUT_MODE,
                                           io->num_components, io->bUseMulipliers);

        if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) )
            return 1;

        inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );
    }

    return 0;
}


/*
    Output AuxInfo:
        reversibility info (to restore orig. structure)
*/
int OutputAUXINFO_ReversibilityInfo( CANON_GLOBALS *pCG,
                                       INCHI_IOSTREAM *out_file,
                                       INCHI_IOSTREAM_STRING *strbuf,
                                       ORIG_STRUCT *pOrigStruct,
                                       OutputINCHI_CtlData *io,
                                       char *pLF, char *pTAB )
{
    if ( !io->bSecondNonTautPass &&
         pOrigStruct && pOrigStruct->num_atoms &&
         pOrigStruct->szAtoms
         && pOrigStruct->szBonds
         && pOrigStruct->szCoord )
    {
        int length, cur_pos, line_len, last_pos, nMaxLineLen;
        char *p;
        nMaxLineLen = inchi_min( 80, strbuf->nAllocatedLength ); /* restrict line length to 80 characters */

        szGetTag( AuxLbl, io->nTag,  io->bTag1 = AL_REVR | io->bFhTag, io->szTag1, &io->bAlways );

        /* Atoms /A: */
        szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_ATMR, io->szTag2, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        inchi_ios_print( out_file, "%s%s", io->szTag2, strbuf->pStr );
        p = pOrigStruct->szAtoms;
        length = (int) strlen( p );
        io->tot_len = strbuf->nUsedLength;
        line_len = nMaxLineLen - io->tot_len;
        for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos )
        {
            if ( length - cur_pos >= line_len )
            {
                last_pos = cur_pos + line_len;
                /* search backward for the nearest first atom letter (always uppercase) */
                while ( cur_pos < last_pos && !isupper( UCINT p[last_pos] ) ) {
                    last_pos --;
                }
            }
            else
            {
                last_pos = length;
            }
            if ( last_pos > cur_pos )
            {
                memcpy( strbuf->pStr + strbuf->nUsedLength, p+cur_pos, last_pos - cur_pos );
                strbuf->pStr[strbuf->nUsedLength + last_pos - cur_pos] = '\0';
                /*strbuf->nUsedLength = strbuf->nUsedLength + last_pos - cur_pos;*/
                inchi_ios_print( out_file, "%s%s", strbuf->pStr, io->bPlainTextTags? "" : "\n" );
            }
            else
            {
                break;
            }
        }
        if ( pLF[0] )
            inchi_ios_print( out_file, "%s", pLF );

        inchi_strbuf_reset( strbuf );

        /*    Bonds /B: */
        szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_BNDR, io->szTag2, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        inchi_ios_print( out_file, "%s%s", io->szTag2, strbuf->pStr );

        p = pOrigStruct->szBonds;
        length = (int) strlen( p );
        line_len = nMaxLineLen - io->tot_len;
        for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos )
        {
            if ( length - cur_pos >= line_len )
            {
                last_pos = cur_pos + line_len - 1;
                /* search backward for the nearest first bond delimiter ";" */
                while ( cur_pos < last_pos && p[last_pos] != ';' )
                {
                    last_pos --;
                }
                if ( cur_pos < last_pos )
                {
                    last_pos ++; /* include ';' at the end of the line */
                }
            }
            else
            {
                last_pos = length;
            }
            if ( last_pos > cur_pos )
            {
                memcpy( strbuf->pStr, p+cur_pos, last_pos - cur_pos );
                strbuf->pStr[last_pos - cur_pos] = '\0';
                strbuf->nUsedLength = last_pos - cur_pos;
                inchi_ios_print( out_file, "%s%s", strbuf->pStr, io->bPlainTextTags? "" : "\n" );
                inchi_strbuf_reset( strbuf );
            }
            else
            {
                break;
            }
        }
        if ( pLF[0] )
        {
            inchi_ios_print( out_file, "%s", pLF );
        }

        /*    Coordinates /C:    */
        szGetTag( AuxLbl, io->nTag,  io->bTag2 = io->bTag1 | AL_XYZR, io->szTag2, &io->bAlways );
        inchi_strbuf_reset( strbuf );
        inchi_ios_print( out_file, "%s%s", io->szTag2, strbuf->pStr );

        p = pOrigStruct->szCoord;
        length = (int) strlen( p );
        line_len = nMaxLineLen - io->tot_len;
        for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos )
        {
            if ( length - cur_pos >= line_len )
            {
                last_pos = cur_pos + line_len - 1;
                /* search backward for the nearest first coord. delimiter ";" */
                while ( cur_pos < last_pos && p[last_pos] != ';' )
                {
                    last_pos --;
                }
                if ( cur_pos < last_pos )
                {
                    last_pos ++; /* include ';' at the end of the line */
                }
            }
            else
            {
                last_pos = length;
            }
            if ( last_pos > cur_pos )
            {
                memcpy( strbuf->pStr, p+cur_pos, last_pos - cur_pos );
                strbuf->pStr[last_pos - cur_pos] = '\0';
                strbuf->nUsedLength = last_pos - cur_pos;
                inchi_ios_print( out_file, "%s%s", strbuf->pStr, io->bPlainTextTags? "" : "\n" );
                inchi_strbuf_reset( strbuf );
            }
            else
            {
                break;
            }
        }

        if ( pLF[0] )
        {
            inchi_ios_print( out_file, "%s", pLF );
        }
    }

    return 0;
}


/*

*/
int OutputAUXINFO_PolymerInfo( CANON_GLOBALS *pCG,
                                  INCHI_IOSTREAM *out_file,
                                  INCHI_IOSTREAM_STRING *strbuf,
                                  ORIG_STRUCT *pOrigStruct,
                                  OutputINCHI_CtlData *io,
                                  char *pLF, char *pTAB )
{
int k, i, q;
OrigAtDataPolymer *p;
OrigAtDataPolymerUnit *u;


    if ( !pOrigStruct )            return 0;
    p = pOrigStruct->polymer;
    if ( !p )                    return 0;

    inchi_strbuf_reset( strbuf );

    inchi_ios_print( out_file, "/Z:" );


    /* Print polymer units data */
    for (i=0; i<p->n; i++)
    {
        /* For each unit u ... */
        u = p->units[i];

        /* print kinds of unit */
        inchi_strbuf_printf( strbuf, "%-d%-d%-d-", u->type, u->subtype, u->conn );
        inchi_strbuf_printf( strbuf, "%-s-", u->smt[0]?u->smt:"n" );

        /* Print unit atoms */
        print_sequence_of_nums_compressing_ranges( u->na, u->alist, strbuf );

        /* Print bonds from unit to otside */
        if ( u->nb > 0 )
        {
            inchi_strbuf_printf( strbuf, "(" );
            for (k=0; k<2*u->nb-1; k++)
                inchi_strbuf_printf( strbuf, "%-d,", u->blist[k] );
            inchi_strbuf_printf( strbuf, "%-d)", u->blist[2*u->nb-1] );
        }


        if ( fabs( -fabs(u->xbr1[0]) + 777777.777 ) > 1.e-7 )
        {
            inchi_strbuf_printf( strbuf, "[" );
            for (q=0; q<3; q++)
                inchi_strbuf_printf( strbuf, "%-f,", u->xbr1[q] );
            inchi_strbuf_printf( strbuf, "%-f]", u->xbr1[3] );
        }
        if ( fabs( -fabs(u->xbr2[0]) + 777777.777 ) > 1.e-7 )
        {
            inchi_strbuf_printf( strbuf, "[" );
            for (q=0; q<3; q++)
                inchi_strbuf_printf( strbuf, "%-f,", u->xbr2[q] );
            inchi_strbuf_printf( strbuf, "%-f]", u->xbr2[3] );
        }

        if ( i < p->n-1 )
            inchi_strbuf_printf( strbuf, ";" );
    }

    inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF );

    return 0;
}


int psbond_min_num_compare( int *bond1, int* bond2 )
{
    int min1 = inchi_min( bond1[0], bond1[1] );
    int min2 = inchi_min( bond2[0], bond2[1] );
    int max1 = inchi_max( bond1[0], bond1[1] );
    int max2 = inchi_max( bond2[0], bond2[1] );

    if ( min1 < min2 )    return -1;
    if ( min1 > min2 )    return 1;
    if ( min1 == min2 )
    {
        if ( max1 < max2 )    return -1;
        if ( max1 > max2 )    return 1;
    }
    return 0;
}


/*

*/
void HidePolymerRelatedInternals( INCHI_IOSTREAM *out )
{
char *s = out->s.pStr, *buf=NULL;
char prev_layer_symbol = '0';
int i, j, ii, nzz, nzz1 = 0,    /* nzz2 = 0, */
    nslash = 0, ncopied = 0,
    in_z = 0, skip = 0;

    nzz1 = CountZzInFormula( s );
    if ( nzz1 == 0 )    return;

    buf = (char *) inchi_calloc( out->s.nUsedLength + 1, sizeof(char) );

    nzz = nzz1;
    in_z = skip = 0;
    for (i=0; i<out->s.nUsedLength; i++)
    {
        int pre_eol = ( i == out->s.nUsedLength - 1 );

        if ( !skip )
        {
            buf[ncopied] = s[i];
            ncopied++;
        }

        if ( in_z && !skip )
        {
            if ( s[i] == '(' )
            {
                skip    = 1;
            }
        }
        else if ( in_z && skip )
        {
            if ( s[i] == '-' )
            {
                skip = 0;
            }
        }

        if ( s[i] == '/' || pre_eol )
        {
            if ( in_z )
                in_z = 0;

            if ( !pre_eol )
            {
                nslash++;
            }

            if ( nslash == 2                ||
                (nslash == 1 && pre_eol)    ||
                ( prev_layer_symbol == 'f' )
                )
            {
                if ( nzz )
                {
                    /* eat Zz's */
                    ii = i;
                    if ( pre_eol )    ii = i + 1;
                    if ( s[ii-1] == 'z' && s[ii-2] == 'Z' )
                    {
                        ncopied        -= 2;
                        for (j=ii-3; j>=0; j--)
                        {
                            if ( s[j] == '.' )
                                break;
                            ncopied--;
                        }
                        ncopied--;
                        if ( !pre_eol )
                            buf[ncopied-1] = '/';
                        else
                            buf[ncopied-1] = '\0';
                    }
                }
            }
            else if ( nslash > 2 || pre_eol  )
            {
                if ( nzz )
                {
                    if ( prev_layer_symbol != 'p' &&
                         prev_layer_symbol != 's' &&
                         prev_layer_symbol != 'f' &&
                         prev_layer_symbol != 'z'
                        )
                    {
                        /* eat nzz last ; if any */
                        int n_eaten = 0;
                        char eatable = ';';
                        if ( prev_layer_symbol == 'm' )
                            eatable = '.';
                        ii = i;
                        if ( pre_eol )
                            ii = i + 1;
                        for (j=ii-1; j>=0; j--)
                        {
                            if ( s[j] == eatable  && n_eaten < nzz )
                            {
                                ncopied--;
                                n_eaten++;
                            }
                            else
                                break;
                        }
                        if ( !pre_eol )
                            buf[ncopied-1] = '/';
                        else
                        {
                            buf[ncopied] = '\0';
                            break;
                        }
                    }
                }
            }

            prev_layer_symbol = s[i+1];
            if ( s[i+1] == 'r' )
            {
                /* nzz = nzz2;    paranoidal */
                nslash    = 1;
            }

            else if ( s[i+1] == 'z' )
            {
                in_z    = 1;
            }
        }
    }


    out->s.nUsedLength = 0;

    inchi_ios_print_nodisplay( out, "%s", buf );

    inchi_free( buf );

    return;
}


int CountZzInFormula( char *s)
{
    int i, j, len, nzz = 0, nslash = 0;

    if ( !s )
        return 0;
    len = strlen(s);
    for (i=0; i< len; i++)
    {
        if ( s[i]=='/' )
            nslash++;
        if ( nslash == 2 )
        {
            if ( s[i-1] != 'z' || s[i-2] != 'Z' )    break;
            for ( j = i-3; j>=0; j-- )
                if ( !isdigit( UCINT s[j]) )
                    break;
            nzz = strtol( s + j +1, NULL, 10);
        }
    }
    return nzz;
}
