/*
FILE:     addcard_util.C
*/
/*
VERSION:  1.600
*/
/*
DATE:     2/3/2005
*/
/*
  Comments and Questions to: sw-help@rcsb.rutgers.edu
*/
/*
COPYRIGHT 1999-2005 Rutgers - The State University of New Jersey

This software is provided WITHOUT WARRANTY OF MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE OR ANY OTHER WARRANTY, EXPRESS OR
IMPLIED.  RUTGERS MAKE NO REPRESENTATION OR WARRANTY THAT THE
SOFTWARE WILL NOT INFRINGE ANY PATENT, COPYRIGHT OR OTHER
PROPRIETARY RIGHT.

The user of this software shall indemnify, hold harmless and defend
Rutgers, its governors, trustees, officers, employees, students,
agents and the authors against any and all claims, suits,
losses, liabilities, damages, costs, fees, and expenses including
reasonable attorneys' fees resulting from or arising out of the
use of this software.  This indemnification shall include, but is
not limited to, any and all claims alleging products liability.
*/
/*
               PDB SOFTWARE LICENSE AGREEMENT

BY CLICKING THE ACCEPTANCE BUTTON OR INSTALLING OR USING 
THIS "SOFTWARE, THE INDIVIDUAL OR ENTITY LICENSING THE  
SOFTWARE ("LICENSEE") IS CONSENTING TO BE BOUND BY AND IS 
BECOMING A PARTY TO THIS AGREEMENT.  IF LICENSEE DOES NOT 
AGREE TO ALL OF THE TERMS OF THIS AGREEMENT
THE LICENSEE MUST NOT INSTALL OR USE THE SOFTWARE.

1. LICENSE AGREEMENT

This is a license between you ("Licensee") and the Protein Data Bank (PDB) 
at Rutgers, The State University of New Jersey (hereafter referred to 
as "RUTGERS").   The software is owned by RUTGERS and protected by 
copyright laws, and some elements are protected by laws governing 
trademarks, trade dress and trade secrets, and may be protected by 
patent laws. 

2. LICENSE GRANT

RUTGERS grants you, and you hereby accept, non-exclusive, royalty-free 
perpetual license to install, use, modify, prepare derivative works, 
incorporate into other computer software, and distribute in binary 
and source code format, or any derivative work thereof, together with 
any associated media, printed materials, and on-line or electronic 
documentation (if any) provided by RUTGERS (collectively, the "SOFTWARE"), 
subject to the following terms and conditions: (i) any distribution 
of the SOFTWARE shall bind the receiver to the terms and conditions 
of this Agreement; (ii) any distribution of the SOFTWARE in modified 
form shall clearly state that the SOFTWARE has been modified from 
the version originally obtained from RUTGERS.  

2. COPYRIGHT; RETENTION OF RIGHTS.  

The above license grant is conditioned on the following: (i) you must 
reproduce all copyright notices and other proprietary notices on any 
copies of the SOFTWARE and you must not remove such notices; (ii) in 
the event you compile the SOFTWARE, you will include the copyright 
notice with the binary in such a manner as to allow it to be easily 
viewable; (iii) if you incorporate the SOFTWARE into other code, you 
must provide notice that the code contains the SOFTWARE and include 
a copy of the copyright notices and other proprietary notices.  All 
copies of the SOFTWARE shall be subject to the terms of this Agreement.  

3. NO MAINTENANCE OR SUPPORT; TREATMENT OF ENHANCEMENTS 

RUTGERS is under no obligation whatsoever to: (i) provide maintenance 
or support for the SOFTWARE; or (ii) to notify you of bug fixes, patches, 
or upgrades to the features, functionality or performance of the 
SOFTWARE ("Enhancements") (if any), whether developed by RUTGERS 
or third parties.  If, in its sole discretion, RUTGERS makes an 
Enhancement available to you and RUTGERS does not separately enter 
into a written license agreement with you relating to such bug fix, 
patch or upgrade, then it shall be deemed incorporated into the SOFTWARE 
and subject to this Agreement. You are under no obligation whatsoever 
to provide any Enhancements to RUTGERS or the public that you may 
develop over time; however, if you choose to provide your Enhancements 
to RUTGERS, or if you choose to otherwise publish or distribute your 
Enhancements, in source code form without contemporaneously requiring 
end users or RUTGERS to enter into a separate written license agreement 
for such Enhancements, then you hereby grant RUTGERS a non-exclusive,
royalty-free perpetual license to install, use, modify, prepare
derivative works, incorporate into the SOFTWARE or other computer
software, distribute, and sublicense your Enhancements or derivative
works thereof, in binary and source code form.

4. FEES.  There is no license fee for the SOFTWARE.  If Licensee
wishes to receive the SOFTWARE on media, there may be a small charge
for the media and for shipping and handling.  Licensee is
responsible for any and all taxes.

5. TERMINATION.  Without prejudice to any other rights, Licensor
may terminate this Agreement if Licensee breaches any of its terms
and conditions.  Upon termination, Licensee shall destroy all
copies of the SOFTWARE.

6. PROPRIETARY RIGHTS.  Title, ownership rights, and intellectual
property rights in the Product shall remain with RUTGERS.  Licensee 
acknowledges such ownership and intellectual property rights and will 
not take any action to jeopardize, limit or interfere in any manner 
with RUTGERS' ownership of or rights with respect to the SOFTWARE.  
The SOFTWARE is protected by copyright and other intellectual 
property laws and by international treaties.  Title and related 
rights in the content accessed through the SOFTWARE is the property 
of the applicable content owner and is protected by applicable law.  
The license granted under this Agreement gives Licensee no rights to such
content.

7. DISCLAIMER OF WARRANTY.  THE SOFTWARE IS PROVIDED FREE OF 
CHARGE, AND, THEREFORE, ON AN "AS IS" BASIS, WITHOUT WARRANTY OF 
ANY KIND, INCLUDING WITHOUT LIMITATION THE WARRANTIES THAT IT 
IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 
OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND 
PERFORMANCE OF THE SOFTWARE IS BORNE BY LICENSEE.  SHOULD THE 
SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, THE LICENSEE AND NOT 
LICENSOR ASSUMES THE ENTIRE COST OF ANY SERVICE AND REPAIR.  
THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF 
THIS AGREEMENT.  NO USE OF THE PRODUCT IS AUTHORIZED HEREUNDER 
EXCEPT UNDER THIS DISCLAIMER.

8. LIMITATION OF LIABILITY.  TO THE MAXIMUM EXTENT PERMITTED BY
APPLICABLE LAW,  IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY 
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 
OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, INCLUDING, 
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK 
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL 
OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF ADVISED OF THE
POSSIBILITY THEREOF. 
*/
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "_maxit.h"
#include "ndb_misclib.h"
#include "erf_category.h"
#include "erf_util.h"
#include "util.h"

#define NUM_NAT_TOKEN   11
#define NUM_GEN_TOKEN   17
#define NUM_REM_TOKEN    6

static const char *nat_token_name[NUM_NAT_TOKEN] = {
            "SRCNCC", "SRCNDS", "SRCNFR", "SRCNN1", "SRCNNM", "SRCNOR",
            "SRCNPL", "SRCNSC", "SRCNSE", "SRCNSP", "SRCNTS"
};

static const char *gen_token_name[NUM_GEN_TOKEN] = {
            "SRCGCC", "SRCGDS", "SRCGE1", "SRCGE2", "SRCGE3", "SRCGFR",
            "SRCGH1", "SRCGHC", "SRCGHN", "SRCGHS", "SRCGHT", "SRCGHV",
            "SRCGNM", "SRCGPL", "SRCGSP", "SRCGTS", "SRCGVE"
};

static const char *remove_token[NUM_REM_TOKEN] = {
            "CAVEAT", "REVDAT", "SPRSDE", "DBREF", "SEQADV", "MODRES"
};

static _ENTITY *realloc_entity(_ENTITY *entity, const int n_size);
static char *get_entity_description(_CHAIN *chain);
static char *get_line_from_string(char *line, char *string);

void _MAXIT::Output_Stbiol()
{
       int i, j, k, op, t[3], len;
       char sym_name[40];
       if (_n_stbiol == 0 || _stbiol == NULL) return;
 
       One_Card_Info_Format *pCard = _CardInfo[STBIOL_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[STBIOL_TOKEN-1].delete_a_card(pCard);
       for (i = 0; i < _n_stbiol; i++) {
            if (!_stbiol[i]) continue;
            pCard = _CardInfo[STBIOL_TOKEN-1].add_a_new_card(NdbToken[STBIOL_TOKEN-1], 0);
            pCard->AddElementToField(i+1, 1);
            pCard->AddElementToField(_stbiol[i]->Biol_ID, 2);
            pCard->AddElementToField(_stbiol[i]->Asym_ID.Text(), 3);
            pCard->AddElementToField(_stbiol[i]->Symmetry, 4);
            if (_stbiol[i]->bbrn != -99999)
                 pCard->AddElementToField(_stbiol[i]->bbrn, 5);
            if (_stbiol[i]->bern != -99999)
                 pCard->AddElementToField(_stbiol[i]->bern, 6);
            if (_stbiol[i]->abrn != -99999)
                 pCard->AddElementToField(_stbiol[i]->abrn, 7);
            if (_stbiol[i]->aern != -99999)
                 pCard->AddElementToField(_stbiol[i]->aern, 8);
            pCard->AddElementToField(_stbiol[i]->NDB_Strand_ID.Text(), 12);
            pCard->AddElementToField(_stbiol[i]->PDB_Strand_ID, 13);
            pCard->AddElementToField(_stbiol[i]->order, 15);
            pCard->AddElementToField(_stbiol[i]->PDB_Strand_ID_Flag, 16);
            
            op = atoi(_stbiol[i]->Symmetry) - 1;
            t[0] = t[1] = t[2] = 0;
            len = strlen(_stbiol[i]->Symmetry);
            for (j = 0; j < len; j++)
                 if (_stbiol[i]->Symmetry[j] == '_') break;
            if (j + 3 < len) {
                 for (k = 0; k < 3; k++)
                      t[k] = _stbiol[i]->Symmetry[j+k+1] - '5';
            }
            sym_name[0] ='\0';
            ndb_get_symmetry_operation_name(_cell, op, t[0], t[1], t[2], sym_name);
            pCard->AddElementToField(sym_name, 14);
       }
}

void _MAXIT::Input_Entity()
{
       int count = 0, i, len;
       One_Card_Info_Format *pCard = NULL, *qCard = NULL;

       pCard = _CardInfo[ENTITY_TOKEN-1].Head();
       while (pCard) {
            count++;
            pCard = pCard->Next();
       }
       if (!count) return;

       if (_entity) {
            for (i = 0; i < _n_entity; i++) {
                 if (_entity[i].Type) delete _entity[i].Type;
                 if (_entity[i].Source) delete _entity[i].Source;
                 if (_entity[i].Description) delete _entity[i].Description;
            }
            delete _entity;
       }
       _n_entity = count;
       _entity = new _ENTITY[_n_entity];

       count = 0;
       pCard = _CardInfo[ENTITY_TOKEN-1].Head();
       while (pCard) {
            _entity[count].Entity_ID = atoi(pCard->Field(1));
            _entity[count].Old_Entity_ID = _entity[count].Entity_ID;
            len = strlen(pCard->Field(2));
            if (len) {
                 _entity[count].Type = new char[len+1];
                 strcpy(_entity[count].Type, pCard->Field(2));
            } else _entity[count].Type = NULL;
            len = strlen(pCard->Field(3));
            if (len) {
                 _entity[count].Source = new char[len+1];
                 strcpy(_entity[count].Source, pCard->Field(3));
            } else _entity[count].Source = NULL;
            _entity[count].Number = atoi(pCard->Field(4));

            qCard = _CardInfo[ENTDES_TOKEN-1].Head();
            while (qCard) {
                 if (!strcmp(pCard->Field(1), qCard->Field(1))) break;
                 qCard = qCard->Next();
            }
            if (qCard) {
                 len = strlen(qCard->Field(3));
                 if (len) {
                      _entity[count].Description = new char[len+1];
                      strcpy(_entity[count].Description, qCard->Field(3));
                 } else _entity[count].Description = NULL;
            } else _entity[count].Description = NULL;

            count++;
            pCard = pCard->Next();
       }
       pCard = _CardInfo[ENTITY_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[ENTITY_TOKEN-1].delete_a_card(pCard);
       pCard = _CardInfo[ENTDES_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[ENTDES_TOKEN-1].delete_a_card(pCard);
}

void _MAXIT::Output_Entity()
{
       if (_n_entity == 0 || _entity == NULL) return;

       One_Card_Info_Format *pCard = _CardInfo[ENTITY_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[ENTITY_TOKEN-1].delete_a_card(pCard);
       pCard = _CardInfo[ENTDES_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[ENTDES_TOKEN-1].delete_a_card(pCard);
       for (int i = 0; i < _n_entity; i++) {
            pCard = _CardInfo[ENTITY_TOKEN-1].add_a_new_card(NdbToken[ENTITY_TOKEN-1], 0);
            pCard->AddElementToField(_entity[i].Entity_ID, 1);
            if (_entity[i].Type)
                 pCard->AddElementToField(_entity[i].Type, 2);
            if (_entity[i].Source)
                 pCard->AddElementToField(_entity[i].Source, 3);
            pCard->AddElementToField(_entity[i].Number, 4);
            if (_entity[i].Description && strcmp(_entity[i].Description, "")) {
                 pCard = _CardInfo[ENTDES_TOKEN-1].
                           add_a_new_card(NdbToken[ENTDES_TOKEN-1], 0);
                 pCard->AddElementToField(_entity[i].Entity_ID, 1);
                 pCard->AddElementToField(_entity[i].Description, 3);
            }
       }
}

void _MAXIT::match_entity_with_molecules()
{
       if (_n_entity == 0 || _entity == NULL ||
           _num_mol == 0 || _molecules == NULL) return;

       for (int i = 0; i < _n_entity; i++) {
            _entity[i].Old_Entity_ID = _entity[i].Entity_ID;
            int Entity_ID = find_entity_index(_entity[i].Entity_ID);
            if (Entity_ID >= 0)
                 _entity[i].Entity_ID = Entity_ID;
       }
}

int _MAXIT::find_entity_index(const int Old_Entity_ID)
{
       int found = 0, Entity_ID = -1, id, entity_id, rowNo, i, j, len;
       CifString ChainID, chain_id, entity;
       ISTable *Table = NULL;
       One_Card_Info_Format *pCard = _CardInfo[STASYM_TOKEN-1].Head();

       while (pCard) {
            if (atoi(pCard->Field(4)) == Old_Entity_ID) {
                 ChainID.Copy(pCard->Field(3));
                 found = 1; break;
            }
            pCard = pCard->Next();
       }
       if (!found && _CifObj) {
            Table = _CifObj->GetTablePtr(_StructureId, "struct_asym");
            if (!is_empty_table(Table)) {
                 id = Table->GetColumnIndex("id");
                 entity_id = Table->GetColumnIndex("entity_id");
                 if (id >= 0 && entity_id >= 0) {
                      rowNo = Table->GetNumRows();
                      for (i = 0; i < rowNo; i++) {
                           get_value(chain_id, Table, id, i);
                           get_value(entity, Table, entity_id, i);
                           if (atoi(entity.Text()) == Old_Entity_ID) {
                                ChainID.Copy(chain_id);
                                found = 1; break;
                           }
                      }
                 }
            }
       }
       if (found) {
            for (i = 0; i < _n_chain_corr; i++) {
                 if (ChainID == _chain_corr[i].old_chn) {
                      ChainID.Copy(_chain_corr[i].new_chn);
                      break;
                 }
            }
       }
       if (!found && _CifObj) {
            Table = _CifObj->GetTablePtr(_StructureId, "entity_poly");
            if (!is_empty_table(Table)) {
                 entity_id = Table->GetColumnIndex("entity_id");
                 id = Table->GetColumnIndex("ndb_chain_id");
                 if (id >= 0 && entity_id >= 0) {
                      rowNo = Table->GetNumRows();
                      for (i = 0; i < rowNo; i++) {
                           get_value(chain_id, Table, id, i);
                           len = strlen(chain_id.Text());
                           for (j = 0; j < len; j++) {
                                if (chain_id.Text()[j] == ' ' || chain_id.Text()[j] == ',') {
                                     chain_id.Text()[j] = '\0'; break;
                                }
                           }
                           get_value(entity, Table, entity_id, i);
                           if (atoi(entity.Text()) == Old_Entity_ID) {
                                ChainID.Copy(chain_id);
                                found = 1; break;
                           }
                      }
                 }
            }
       }
       if (!found) return Entity_ID;

       _CHAIN *chain = _molecules[_rep_mol]->GetFirstChain();
       while (chain) {
            if (ChainID == chain->ChainID()) {
                 Entity_ID = chain->entity_id();
                 break;
            }
            chain = _molecules[_rep_mol]->GetNextChain();
       }
       return Entity_ID;
}

void _MAXIT::get_entity_card()
{
       int asym_no = 0, SerialNo = 0, entity_id = 0, NCHAIN_TOKEN;
       int i = 0, j = 0, k = 0, len = 0, n_tmp, found, syn_token;
       int nat_token[NUM_NAT_TOKEN], gen_token[NUM_GEN_TOKEN];
       char polymer[80], method[20];
       _CHAIN *chain = NULL;
       _ENTITY *tmp = NULL;
       One_Card_Info_Format *pCard = NULL;

       if (_num_mol == 0 || _molecules == NULL) return;

       for (i = 0; i < NUM_NAT_TOKEN; i++)
            nat_token[i] = find_card_id(nat_token_name[i]);
       for (i = 0; i < NUM_GEN_TOKEN; i++)
            gen_token[i] = find_card_id(gen_token_name[i]);
       syn_token = find_card_id("SRCSYN");

       chain = _molecules[_rep_mol]->GetFirstChain();
       while (chain) {
            if (chain->chain_type() == ATOMN_TOKEN) {
                 asym_no++;
                 if (chain->biol_id() == 1) SerialNo++;
            }
            chain = _molecules[_rep_mol]->GetNextChain();
       }
       if (asym_no || SerialNo) {
            NCHAIN_TOKEN = find_card_id("NCHAIN");
            pCard = _CardInfo[NCHAIN_TOKEN].Head();
            if (pCard == NULL) pCard = _CardInfo[NCHAIN_TOKEN].
                     add_a_new_card(NdbToken[NCHAIN_TOKEN], 0);
            if (asym_no && !strcmp(pCard->Field(2), ""))
                 pCard->AddElementToField(asym_no, 2);
            if (SerialNo && !strcmp(pCard->Field(3), ""))
                 pCard->AddElementToField(SerialNo, 3);
       }

       pCard = _CardInfo[STASYM_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[STASYM_TOKEN-1].delete_a_card(pCard);

       n_tmp = _n_entity;
       tmp = _entity;
       _n_entity = 0;
       _entity = NULL;
       chain = _molecules[_rep_mol]->GetFirstChain();
       i = 0;
       while (chain) {
            i++;
            entity_id = chain->entity_id();
            pCard = _CardInfo[STASYM_TOKEN-1].add_a_new_card(NdbToken[STASYM_TOKEN-1], 0);
            pCard->AddElementToField(i, 1);
            pCard->AddElementToField(chain->ChainID(), 3);
            pCard->AddElementToField(entity_id, 4);

            for (j = 0; j < _n_entity; j++) {
                 if (_entity[j].Entity_ID == entity_id) break;
            }
            if (j < _n_entity) {
                 if (!strcmp(chain->SeqRes(0)->Field[0], "HOH") ||
                     !strcmp(chain->SeqRes(0)->Field[0], "DOD"))
                      _entity[j].Number += chain->ResidueNumbers();
                 else _entity[j].Number++;
            } else {
                 _entity = realloc_entity(_entity, _n_entity);
                 _n_entity++;
                 _entity[j].Entity_ID = entity_id;
                 if (!strcmp(chain->SeqRes(0)->Field[0], "HOH") ||
                     !strcmp(chain->SeqRes(0)->Field[0], "DOD"))
                      _entity[j].Number = chain->ResidueNumbers();
                 else _entity[j].Number = 1;
            }
            polymer[0] = '\0'; method[0] = '\0';
            if (chain->chain_type() == ATOMP_TOKEN ||
                chain->chain_type() == ATOMS_TOKEN ||
                chain->chain_type() == ATOMN_TOKEN) {
                 strcpy(polymer, NDB_POLYMER_TEXT);
                 found = 0;
                 for (k = 0; k < NUM_NAT_TOKEN; k++) {
                      if (nat_token[k] < 0) continue;
                      pCard = _CardInfo[nat_token[k]].Head();
                      while (pCard) {
                           if (entity_id == atoi(pCard->Field(1))) {
                                found = 1;
                                break;
                           }
                           pCard = pCard->Next();
                      }
                      if (found) break;
                 }
                 if (found) strcpy(method, "nat");
                 else {
                      for (k = 0; k < NUM_GEN_TOKEN; k++) {
                           if (gen_token[k] < 0) continue;
                           pCard = _CardInfo[gen_token[k]].Head();
                           while (pCard) {
                                if (entity_id == atoi(pCard->Field(1))) {
                                     found = 1;
                                     break;
                                }
                                pCard = pCard->Next();
                           }
                           if (found) break;
                      }
                      if (found) strcpy(method, "man");
                      else {
                           if (syn_token >= 0) {
                                pCard = _CardInfo[syn_token].Head();
                                while (pCard) {
                                     if (entity_id == atoi(pCard->Field(1))) {
                                          found = 1;
                                          break;
                                     }
                                     pCard = pCard->Next();
                                }
                           }
                           if (found) strcpy(method, "syn");
                           else {
                                strcpy(method, "man");
                                if (chain->chain_type() == ATOMN_TOKEN)
                                     strcpy(method, "syn");
                           }
                      }
                 }
                 if (chain->chain_type() == ATOMS_TOKEN &&
                     chain->ResidueNumbers() == 1)
                      strcpy(polymer, NDB_NON_POLYMER_TEXT);
                 for (k = 0; k < n_tmp; k++) {
                      if (tmp[k].Entity_ID == _entity[j].Entity_ID) {
                           if (tmp[k].Source && strcmp(tmp[k].Source, "")) {
                                _entity[j].Source = tmp[k].Source;
                                tmp[k].Source = NULL;
                           }
                           if (tmp[k].Description && strcmp(tmp[k].Description, "")) {
                                _entity[j].Description = tmp[k].Description;
                                tmp[k].Description = NULL;
                           }
                           break;
                      }
                 }
            } else if (chain->chain_type() == HETAS_TOKEN &&
                 (!strcmp(chain->SeqRes(0)->Field[0], "HOH") ||
                  !strcmp(chain->SeqRes(0)->Field[0], "DOD"))) {
                 strcpy(polymer, NDB_WATER_TEXT);
                 strcpy(method, "nat");
            } else {
                 strcpy(polymer, NDB_NON_POLYMER_TEXT);
                 strcpy(method, "syn");
            }

            len = strlen(polymer);
            if (len) {
                 if (_entity[j].Type) delete [] _entity[j].Type;
                 _entity[j].Type = new char[len+1];
                 strcpy(_entity[j].Type, polymer);
            }
            len = strlen(method);
            if (len && !_entity[j].Source) {
                 _entity[j].Source = new char[len+1];
                 strcpy(_entity[j].Source, method);
            } else ndb_set_string_to_lower(_entity[j].Source);
            if (!_entity[j].Description) {
                 if (chain->chain_type() == ATOMP_TOKEN)
                      _entity[j].Description = get_protein_descriptor(chain);
                 else _entity[j].Description = get_entity_description(chain);
            }
            chain = _molecules[_rep_mol]->GetNextChain();
       }
       if (tmp) {
            for (i = 0; i < n_tmp; i++) {
                 if (tmp[i].Type) delete [] tmp[i].Type;
                 if (tmp[i].Source) delete [] tmp[i].Source;
                 if (tmp[i].Description) delete [] tmp[i].Description;
            }
            delete [] tmp;
       }
}

char* _MAXIT::get_protein_descriptor(_CHAIN *chain)
{
       char *buff = NULL;
       int len;
       One_Card_Info_Format *pCard = _CardInfo[PROTEI_TOKEN-1].Head();
       while (pCard) {
            if (atoi(pCard->Field(1)) == chain->entity_id()) break;
            pCard = pCard->Next();
       }
       if (pCard && strcmp(pCard->Field(3), "")) {
            len = strlen(pCard->Field(3)) + 12;
            buff = new char[len];
            sprintf(buff, "%s (%s)", NDB_PR_TEXT, pCard->Field(3));
       } else {
/*
            buff = new char[22];
            sprintf(buff, "%s (%d-MER)", NDB_PR_TEXT, chain->ResidueNumbers());
*/
            buff = new char[2];
            strcpy(buff, "");
       }
       return buff;
}

void _MAXIT::get_compnd_card()
{
       One_Card_Info_Format *pCard = _CardInfo[COMPND_TOKEN-1].Head();
       if (pCard) return;

       if (_num_mol == 0 || _molecules == NULL) return;
       _CHAIN *chain = _molecules[0]->GetFirstChain();
       if (!chain) return;

       int i, len;
       char CompndString[10000], proteinName[10000];
       CompndString[0] = '\0';
       memset(CompndString, 0, MxLen);
       if (chain->chain_type() == ATOMN_TOKEN) {
            ndb_get_protein_name(proteinName, 0);
            if (strcmp(proteinName, "")) {
                 if (chain->na_type() == ATOMN_TYPE_DNA_ONLY)
                      sprintf(CompndString, "%s/DNA Complex", proteinName);
                 else if (chain->na_type() == ATOMN_TYPE_RNA_ONLY ||
                          chain->na_type() == ATOMN_TYPE_TRNA)
                      sprintf(CompndString, "%s/RNA Complex", proteinName);
                 else if (chain->na_type() < 0) {
                      for (i = 0; i < _n_entity; i++) {
                           if (_entity[i].Entity_ID == chain->entity_id())
                                break;
                      }
                      if (i < _n_entity && _entity[i].Description &&
                         (strstr(_entity[i].Description, "RIBOSOM") ||
                          strstr(_entity[i].Description, "ribiosom") ||
                          strstr(_entity[i].Description, "RNA") ||
                          strstr(_entity[i].Description, "rna")))
                           sprintf(CompndString, "%s/RNA Complex", proteinName);
                      else sprintf(CompndString, "%s/DNA Complex", proteinName);
                 }
            } else {
                 chain->get_descriptor(CompndString);
                 _CHAIN *chain1 = _molecules[0]->GetNextChain();
                 if (chain1 && chain1->chain_type() == ATOMN_TOKEN &&
                     chain1->entity_id() != chain->entity_id()) {
                      strcat(CompndString, ", ");
                      len = strlen(CompndString);
                      chain1->get_descriptor(&CompndString[len]);
                 }
            }
            get_descriptor_ligand(CompndString);
       } else {
            if (_CardInfo[PROTEI_TOKEN-1].Head())
                 ndb_get_protein_name(CompndString, 1);
            else if (_CardInfo[HEADER_TOKEN-1].Head())
                 strcpy(CompndString, _CardInfo[HEADER_TOKEN-1].Head()->Field(1));
       }

       pCard = _CardInfo[COMPND_TOKEN-1].add_a_new_card(NdbToken[COMPND_TOKEN-1], 0);
       pCard->AddElementToField(CompndString, 2);
}

void _MAXIT::ndb_get_protein_name(char *name, const int add_ec)
{
       int len, i, str_len;
       One_Card_Info_Format *pCard = NULL, *qCard = NULL;

       name[0] = '\0';
       pCard = _CardInfo[PROTEI_TOKEN-1].Head();
       while (pCard) {
            if (strcmp(pCard->Field(3), "") && 
               !strstr(name, pCard->Field(3))) {
                 qCard = _CardInfo[ECNUM_TOKEN-1].Head();
                 while (qCard) {
                      if (!strcmp(qCard->Field(1), pCard->Field(1))) break;
                      qCard = qCard->Next();
                 }
                 len = strlen(name);
                 if (add_ec && qCard && qCard->Field(2) && strcmp(qCard->Field(2), "")) {
                      str_len = strlen(qCard->Field(2));
                      for (i = 0; i < str_len; i++) {
                           if (isdigit(qCard->Field(2)[i])) break;
                      }
                      if (i < str_len)
                           sprintf(&name[len], "%s (E.C.%s), ", pCard->Field(3), &(qCard->Field(2)[i]));
                      else sprintf(&name[len], "%s, ", pCard->Field(3));
                 } else sprintf(&name[len], "%s, ", pCard->Field(3));
            }
            pCard = pCard->Next();
       }

       str_len = strlen(name);
       for (len = str_len - 1; len >= 0; len--) {
            if (name[len] == ',') {
                 name[len] = '\0'; break;
            }
       }

       if (name[0] == '\0') {
            _CHAIN *chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() == ATOMP_TOKEN) {
                      strcpy(name, "PROTEIN"); break;
                 }
                 chain = _molecules[0]->GetNextChain();
            }
       }
}

void _MAXIT::get_descriptor_ligand(char *CompndString)
{
       char ligand_name[800];
       int  tokenid[2], i, len;
       One_Card_Info_Format *pCard = NULL;

       ligand_name[0] = '\0';
       tokenid[0] = DCHEM_TOKEN-1;
       tokenid[1] = INHIBT_TOKEN-1;

       for (i = 0; i < 2; i++) {
            pCard = _CardInfo[tokenid[i]].Head();
            while (pCard) {
                 if (strcmp(pCard->Field(3), "") &&
                    !strstr(ligand_name, pCard->Field(3))) {
                      len = strlen(ligand_name);
                      sprintf(&ligand_name[len], ", %s",
                           pCard->Field(3));
                 }
                 pCard = pCard->Next();
            }
       }
       strcat(CompndString, ligand_name);
}

void _MAXIT::get_origx_card()
{
       One_Card_Info_Format *pCard = _CardInfo[ORIGX_TOKEN-1].Head();
       if (pCard) return;

       pCard = _CardInfo[ORIGX_TOKEN-1].add_a_new_card(NdbToken[ORIGX_TOKEN-1], 0);
       pCard->AddElementToField("1.000000", 1);
       pCard->AddElementToField("0.000000", 2);
       pCard->AddElementToField("0.000000", 3);
       pCard->AddElementToField("0.00000", 4);
       
       pCard = _CardInfo[ORIGX_TOKEN-1].add_a_new_card(NdbToken[ORIGX_TOKEN-1], 0);
       pCard->AddElementToField("0.000000", 1);
       pCard->AddElementToField("1.000000", 2);
       pCard->AddElementToField("0.000000", 3);
       pCard->AddElementToField("0.00000", 4);

       pCard = _CardInfo[ORIGX_TOKEN-1].add_a_new_card(NdbToken[ORIGX_TOKEN-1], 0);
       pCard->AddElementToField("0.000000", 1);
       pCard->AddElementToField("0.000000", 2);
       pCard->AddElementToField("1.000000", 3);
       pCard->AddElementToField("0.00000", 4);
}

void _MAXIT::get_scale_card()
{
       int i, j;
       char buffer[10];
       One_Card_Info_Format *pCard = NULL;

       if (_cell == NULL) return;

       i = 0;
       pCard = _CardInfo[SCALE_TOKEN-1].Head();
       while (pCard) {
            for (j = 1; j < 5; j++) {
                 if (!strcmp(pCard->Field(j), "")) break;
            }
            if (j < 5) break;
            i++;
            pCard = pCard->Next();
       }
       if (i == 3) return;
 
       pCard = _CardInfo[SCALE_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[SCALE_TOKEN-1].delete_a_card(pCard);

       if (_cell->is_artifical) {
            pCard = _CardInfo[SCALE_TOKEN-1].add_a_new_card(NdbToken[SCALE_TOKEN-1], 0);
            pCard->AddElementToField("1.000000", 1);
            pCard->AddElementToField("0.000000", 2);
            pCard->AddElementToField("0.000000", 3);
            pCard->AddElementToField("0.00000", 4);
            
            pCard = _CardInfo[SCALE_TOKEN-1].add_a_new_card(NdbToken[SCALE_TOKEN-1], 0);
            pCard->AddElementToField("0.000000", 1);
            pCard->AddElementToField("1.000000", 2);
            pCard->AddElementToField("0.000000", 3);
            pCard->AddElementToField("0.00000", 4);
     
            pCard = _CardInfo[SCALE_TOKEN-1].add_a_new_card(NdbToken[SCALE_TOKEN-1], 0);
            pCard->AddElementToField("0.000000", 1);
            pCard->AddElementToField("0.000000", 2);
            pCard->AddElementToField("1.000000", 3);
            pCard->AddElementToField("0.00000", 4);
       } else {
            for (i = 0; i < 3; i++) {
                 pCard = _CardInfo[SCALE_TOKEN-1].
                            add_a_new_card(NdbToken[SCALE_TOKEN-1], 0);
                 for (j = 0; j < 3; j++) {
                      if (fabs(_cell->o_to_f[i][j]) < 0.000001)
                           strcpy(buffer, "0.000000");
                      else sprintf(buffer, "%.6f", _cell->o_to_f[i][j]);
                      pCard->AddElementToField(buffer, j + 1);
                 }
                 pCard->AddElementToField("0.00000", 4);
            }
       }
}

void _MAXIT::get_cryst_card()
{
       One_Card_Info_Format *pCard = _CardInfo[CRYST1_TOKEN-1].Head();
       if (pCard) {
            if (_cell->is_artifical) {
                 if (!strcmp(pCard->Field(1), "")) pCard->AddElementToField("1.000", 1);
                 if (!strcmp(pCard->Field(2), "")) pCard->AddElementToField("1.000", 2);
                 if (!strcmp(pCard->Field(3), "")) pCard->AddElementToField("1.000", 3);
                 if (!strcmp(pCard->Field(4), "")) pCard->AddElementToField("90.00", 4);
                 if (!strcmp(pCard->Field(5), "")) pCard->AddElementToField("90.00", 5);
                 if (!strcmp(pCard->Field(6), "")) pCard->AddElementToField("90.00", 6);
                 if (!strcmp(pCard->Field(7), "")) pCard->AddElementToField("P 1", 7);
                 if (!strcmp(pCard->Field(8), "")) pCard->AddElementToField(1, 8);
            }
            return;
       }

       if (_cell == NULL) return;

       pCard = _CardInfo[CRYST1_TOKEN-1].add_a_new_card(NdbToken[CRYST1_TOKEN-1], 0);
       if (_cell->is_artifical) {
            pCard->AddElementToField("1.000", 1);
            pCard->AddElementToField("1.000", 2);
            pCard->AddElementToField("1.000", 3);
            pCard->AddElementToField("90.00", 4);
            pCard->AddElementToField("90.00", 5);
            pCard->AddElementToField("90.00", 6);
            pCard->AddElementToField("P 1", 7);
            pCard->AddElementToField(1, 8);
       } else {
            char buffer[15];
            sprintf(buffer, "%-9.3f", _cell->a);
            ndb_clean_string(buffer);
            pCard->AddElementToField(buffer, 1);
            sprintf(buffer, "%-9.3f", _cell->b);
            ndb_clean_string(buffer);
            pCard->AddElementToField(buffer, 2);
            sprintf(buffer, "%-9.3f", _cell->c);
            ndb_clean_string(buffer);
            pCard->AddElementToField(buffer, 3);
            sprintf(buffer, "%-7.2f", _cell->alpha);
            ndb_clean_string(buffer);
            pCard->AddElementToField(buffer, 4);
            ndb_clean_string(buffer);
            sprintf(buffer, "%-7.2f", _cell->beta);
            ndb_clean_string(buffer);
            pCard->AddElementToField(buffer, 5);
            sprintf(buffer, "%-7.2f", _cell->gamma);
            ndb_clean_string(buffer);
            pCard->AddElementToField(buffer, 6);
            pCard->AddElementToField(_cell->space_group, 7);
            if (_cell->z) pCard->AddElementToField(_cell->z, 8);
            pCard->AddElementToField(_cell->full_space_group, 10);
       }
}
/*
void _MAXIT::get_expdta_card()
{
       One_Card_Info_Format *pCard = _CardInfo[EXPDTA_TOKEN-1].Head();
       if (pCard) return;

       pCard = _CardInfo[EXPDTA_TOKEN-1].
                add_a_new_card(NdbToken[EXPDTA_TOKEN-1], 0);
       pCard->AddElementToField("X-RAY DIFFRACTION", 2);
}
*/
void _MAXIT::get_title_card()
{
       int caid_id = find_card_id("TITLE");
       One_Card_Info_Format *pCard = _CardInfo[caid_id].Head();
       if (pCard) return;

       int jrnl_id = find_jrnl_id("TITL");
       One_Card_Info_Format *qCard = _JrnlCardInfo[jrnl_id].Head();
       if (qCard == NULL) return;

       pCard = _CardInfo[caid_id].add_a_new_card(NdbToken[caid_id], 0);
       pCard->AddElementToField(qCard->Field(4), 2);
}

void _MAXIT::get_dchem_and_inhibt_cards()
{
       if (_num_mol == 0 || _molecules == NULL) return;
       int DNOMOL_TOKEN = find_card_id("DNOMOL");

       int i, j = 0, num = 0, serial_no = 0, card_id = -1, chain_type = -1;
       int NSOLVT_TOKEN, NION_TOKEN, water_no;
       One_Card_Info_Format *pCard = NULL, *qCard = NULL;
       _CHAIN *chain = NULL;
       _RESIDUE *residue = NULL;
       ConnectFormat *drug = NULL;
       for (i = 0; i < 2; i++) {
            card_id = INHIBT_TOKEN;
            chain_type = HETAIN_TOKEN;
            if (i) {
                 card_id = DCHEM_TOKEN;
                 chain_type = HETAD_TOKEN;
            }

            pCard = _CardInfo[card_id-1].Head();
            if (pCard) continue;

            chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() != chain_type) {
                      chain = _molecules[0]->GetNextChain();
                      continue;
                 }
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      drug = find_drug(residue->ResName());
                      if (!drug) {
                           residue = chain->GetNextResidue();
                           continue;
                      }

                      pCard = _CardInfo[card_id-1].Head();
                      serial_no = 0;
                      while (pCard) {
                           pCard->AddElementToField(++serial_no, 1);
                           if (drug->chemical_name() &&
                               !strcmp(pCard->Field(3), drug->chemical_name())) break;
                           pCard = pCard->Next();
                      }
                      if (pCard) {
                           if (card_id == INHIBT_TOKEN) {
                                num = atoi(pCard->Field(4));
                                pCard->AddElementToField(num + 1, 4);
                           } else if (card_id == DCHEM_TOKEN) {
                                qCard = _CardInfo[DNOMOL_TOKEN].Head();
                                while (qCard) {
                                     if (!strcmp(qCard->Field(1), pCard->Field(1))) break;
                                     qCard = qCard->Next();
                                }
                                if (qCard) {
                                     num = atoi(qCard->Field(2));
                                     qCard->AddElementToField(num + 1, 2);
                                } else {
                                     qCard = _CardInfo[DNOMOL_TOKEN].
                                               add_a_new_card(NdbToken[DNOMOL_TOKEN], 0);
                                     qCard->AddElementToField(serial_no, 1);
                                     qCard->AddElementToField(1, 2);
                                }
                           }
                      } else {
                           pCard = _CardInfo[card_id-1].add_a_new_card(NdbToken[card_id-1], 0);
                           pCard->AddElementToField(++serial_no, 1);
                           if (drug->chemical_name())
                                pCard->AddElementToField(drug->chemical_name(), 3);
                           if (card_id == INHIBT_TOKEN) {
                                pCard->AddElementToField(1, 4);
                                sprintf(pCard->Field(4), "%d", 1);
                           } else if (card_id == DCHEM_TOKEN) {
                                qCard = _CardInfo[DNOMOL_TOKEN].
                                        add_a_new_card(NdbToken[DNOMOL_TOKEN], 0);
                                qCard->AddElementToField(serial_no, 1);
                                qCard->AddElementToField(1, 2);
                           }
                      }
                      residue = chain->GetNextResidue();
                 }
                 chain = _molecules[0]->GetNextChain();
            }
       }

       NSOLVT_TOKEN = find_card_id("NSOLVT");
       water_no = 0;
       pCard = _CardInfo[NSOLVT_TOKEN].Head();
       if (!pCard || !strcmp(pCard->Field(3), "")) {
            water_no = 0;
            chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() == HETAS_TOKEN &&
                     (!strcmp(chain->SeqRes(0)->Field[0], "HOH") ||
                      !strcmp(chain->SeqRes(0)->Field[0], "DOD"))) {
                       water_no += chain->ResidueNumbers();
                 }
                 chain = _molecules[0]->GetNextChain();
            }
            if (water_no > 0) {
                 if (!pCard) pCard = _CardInfo[NSOLVT_TOKEN].
                           add_a_new_card(NdbToken[NSOLVT_TOKEN], FALSE);
                 pCard->AddElementToField("water", 2);
                 pCard->AddElementToField(water_no, 3);
            }
       }

       pCard = _CardInfo[PROTEI_TOKEN-1].Head();
       if (!pCard) {
            chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() == ATOMP_TOKEN) {
                      pCard = _CardInfo[PROTEI_TOKEN-1].Head();
                      while (pCard) {
                           if (atoi(pCard->Field(1)) == chain->entity_id()) break;
                           pCard = pCard->Next();
                      }
                      if (!pCard) pCard = _CardInfo[PROTEI_TOKEN-1].
                              add_a_new_card(NdbToken[PROTEI_TOKEN-1], FALSE);
                      if (!strcmp(pCard->Field(1), ""))
                           pCard->AddElementToField(chain->entity_id(), 1);
                      if (!strcmp(pCard->Field(3), "")) {
                           for (j = 0; j < _n_entity; j++) {
                                if (_entity[j].Entity_ID == chain->entity_id()) {
                                     if (_entity[j].Description &&
                                         strcmp(_entity[j].Description, "")) {
                                          pCard->AddElementToField(_entity[j].Description, 3);
                                     }
                                     break;
                                }
                           }
                      }
                      num = atoi(pCard->Field(4)) + 1;
                      pCard->AddElementToField(num, 4);
                 }
                 chain = _molecules[0]->GetNextChain();
            }
       }

       NION_TOKEN = find_card_id("NION");
       pCard = _CardInfo[NION_TOKEN].Head();
       if (!pCard) {
            chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() == HETAI_TOKEN ||
                     chain->chain_type() == HETIC_TOKEN) {
                      pCard = _CardInfo[NION_TOKEN].Head();
                      while (pCard) {
                           if (atoi(pCard->Field(1)) == chain->entity_id()) break;
                           pCard = pCard->Next();
                      }
                      if (!pCard) pCard = _CardInfo[NION_TOKEN].
                              add_a_new_card(NdbToken[NION_TOKEN], FALSE);
                      if (!strcmp(pCard->Field(1), ""))
                           pCard->AddElementToField(chain->entity_id(), 1);
                      if (!strcmp(pCard->Field(2), "")) {
                           for (j = 0; j < _n_entity; j++) {
                                if (_entity[j].Entity_ID == chain->entity_id()) {
                                     if (_entity[j].Description &&
                                         strcmp(_entity[j].Description, "")) {
                                          pCard->AddElementToField(_entity[j].Description, 2);
                                     }
                                     break;
                                }
                           }
                      }
                      num = atoi(pCard->Field(3)) + 1;
                      pCard->AddElementToField(num, 3);
                 }
                 chain = _molecules[0]->GetNextChain();
            }
       }
}

void _MAXIT::get_het_card()
{
       int i = 0, j = 0, serialNo = 0, num = 0, len;
       char PDB_ID[15], buff[100], buffer[20], *p = NULL;
       _ATOM *atom = NULL;
       _RESIDUE *residue = NULL;
       _CHAIN *chain = NULL;
       ConnectFormat *drug = NULL;
       One_Card_Info_Format *pCard = NULL, *pHetNam = NULL, *pHetSyn = NULL;
       if (_num_mol == 0 || _molecules == NULL) return;

       if (_het) delete [] _het;
       _het = NULL; _n_het = 0;
       pCard = _CardInfo[HET_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[HET_TOKEN-1].delete_a_card(pCard);
       pCard = _CardInfo[FORMUL_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[FORMUL_TOKEN-1].delete_a_card(pCard);
       pCard = _CardInfo[HETNAM_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[HETNAM_TOKEN-1].delete_a_card(pCard);
       pCard = _CardInfo[HETSYN_TOKEN-1].Head();
       while (pCard) pCard = _CardInfo[HETSYN_TOKEN-1].delete_a_card(pCard);

       PDB_ID[0] = '\0';
       pCard = _CardInfo[PDBFIL_TOKEN-1].Head();
       if (pCard) strcpy(PDB_ID, pCard->Field(1));

       chain = _molecules[_rep_mol]->GetFirstChain();
       while (chain) {
            serialNo++;
            if (chain->chain_type() == HETAS_TOKEN &&
                (!strcmp(chain->SeqRes(0)->Field[0], "HOH") ||
                 !strcmp(chain->SeqRes(0)->Field[0], "DOD"))) {
                 if (!strcmp(chain->SeqRes(0)->Field[0], "HOH"))
                      insert_formul_card("HOH", serialNo, chain->ResidueNumbers());
                 else insert_formul_card("DOD", serialNo, chain->ResidueNumbers());
                 chain = _molecules[_rep_mol]->GetNextChain();
                 continue;
            }

            for (i = 0; i < chain->SeqLen(); i++) {
                 if (chain->SeqRes(i)->Deleted) continue;
                 residue = chain->SeqRes(i)->Residue;
                 if (!residue) continue;

                 atom = residue->GetFirstAtom();
                 if (!is_a_standard_residue(residue->ResName())) {
                      insert_formul_card(residue->ResName(), serialNo, 1);
                      insert_het_card(chain, residue->ResName(), atom->pdb_resnum(),
                            atom->ins_code(), residue->AtomNumbers());
                      if ((chain->chain_type() == ATOMP_TOKEN ||
                           chain->chain_type() == ATOMN_TOKEN) &&
                           strcmp(residue->ResName(), "ACE") &&
                           strcmp(residue->ResName(), "NH2") &&
                           strcmp(residue->ResName(), "NH3") &&
                           strcmp(residue->ResName(), "CH2")) {
                           drug = find_drug(residue->ResName());
                           if (drug && drug->parent_residue() &&
                               strcmp(drug->parent_residue(), "")) {
                                memset(buff, 0, 100);
                                strcpy(buff, drug->parent_residue());
                                len = strlen(buff);
                                for (j = 0; j < len; j++) {
                                     if (buff[j] == ',' || buff[j] == ';') buff[j] = ' ';
                                }
                                ndb_clean_string(buff);
                                p = buff;
                                while (p) {
                                     p = get_word(p, buffer);
                                     if (!strcmp(buffer, "")) continue;
                                     if (strlen(drug->chemical_name()) < 40)
                                          Insert_a_Modres(PDB_ID, chain->index(),
                                               chain->SeqRes(i)->ResIndex,
                                               buffer, // drug->parent_residue(),
                                               drug->chemical_name());
                                     else Insert_a_Modres(PDB_ID, chain->index(),
                                               chain->SeqRes(i)->ResIndex,
                                               buffer, "");
                                               // drug->parent_residue(), "");
                                }
                           }
                      }
                 } else if (is_a_standard_residue(residue->ResName()) &&
                            chain->chain_type() != ATOMP_TOKEN &&
                            chain->chain_type() != ATOMN_TOKEN) {
                      insert_formul_card(residue->ResName(), serialNo, 1);
                      insert_het_card(chain, residue->ResName(), atom->pdb_resnum(),
                            atom->ins_code(), residue->AtomNumbers());
                 }
            }
            chain = _molecules[_rep_mol]->GetNextChain();
       }
 
       serialNo = 0;
       pCard = _CardInfo[FORMUL_TOKEN-1].Head();
       while (pCard) {
            num = atoi(pCard->Field(5));
            if (!strcmp(pCard->Field(2), "UNL")) {
                 pHetNam = _CardInfo[HETNAM_TOKEN-1].
                            add_a_new_card(NdbToken[HETNAM_TOKEN-1], 0);
                 pHetNam->AddElementToField(pCard->Field(2), 2);
                 pHetNam->AddElementToField("UNKNOWN LIGAND", 3);
                 pHetNam->AddElementToField(++serialNo, 4);
                 pCard = _CardInfo[FORMUL_TOKEN-1].delete_a_card(pCard);
            } else if (!strcmp(pCard->Field(2), "HOH")) {
                 pCard->AddElementToField("*", 4);
                 if (num > 1) {
                      sprintf(buffer, "%d(H2 O1)", num);
                      pCard->AddElementToField(buffer, 5);
                 } else pCard->AddElementToField("(H2 O1)", 5);
                 pCard = pCard->Next();
            } else {
                 drug = find_drug(pCard->Field(2));
                 if (drug) {
                      buff[0] = '\0';
                      if (num > 1) {
                           if (drug->formal_charge() &&
                               strcmp(drug->formal_charge(), "") &&
                               strcmp(drug->formal_charge(), "0"))
                                sprintf(buff, "%d(%s %s)", num, drug->formula(),
                                                 drug->formal_charge());
                           else sprintf(buff, "%d(%s)", num, drug->formula());
                      } else {
                           if (drug->formal_charge() &&
                               strcmp(drug->formal_charge(), "") &&
                               strcmp(drug->formal_charge(), "0"))
                                sprintf(buff, "%s %s", drug->formula(),
                                                drug->formal_charge());
                           else strcpy(buff, drug->formula());
                      }
                      pCard->AddElementToField(buff, 5);

                      if (drug->chemical_name() &&
                          strcmp(drug->chemical_name(), "")) {
                           pHetNam = _CardInfo[HETNAM_TOKEN-1].
                                      add_a_new_card(NdbToken[HETNAM_TOKEN-1], 0);
                           pHetNam->AddElementToField(pCard->Field(2), 2);
                           pHetNam->AddElementToField(drug->chemical_name(), 3);
                           pHetNam->AddElementToField(++serialNo, 4);
                      }

                      if (drug->synonym() && strcmp(drug->synonym(), "")) {
                           pHetSyn = _CardInfo[HETSYN_TOKEN-1].
                                      add_a_new_card(NdbToken[HETSYN_TOKEN-1], 0);
                           pHetSyn->AddElementToField(pCard->Field(2), 2);
                           pHetSyn->AddElementToField(drug->synonym(), 3);
                      }
                 } else pCard->AddElementToField("", 5);
                 pCard = pCard->Next();
            }
       }
}

void _MAXIT::insert_formul_card(const char *hetID, const int serialNo, const int num)
{
       One_Card_Info_Format *pCard = NULL;

       if (!strcmp(hetID, "DUM") || !strcmp(hetID, "UNK") ||
           !strcmp(hetID, "N") /* || !strcmp(hetID, "UNX") ||
           !strcmp(hetID, "UNL") */ ) return;

       pCard = _CardInfo[FORMUL_TOKEN-1].Head();
       while (pCard) {
            if (!strcmp(pCard->Field(2), hetID)) break;
            pCard = pCard->Next();
       }
       if (pCard) {
            int n = atoi(pCard->Field(5)) + num;
            pCard->AddElementToField(n, 5);
       } else {
            pCard = _CardInfo[FORMUL_TOKEN-1].
                 add_a_new_card(NdbToken[FORMUL_TOKEN-1], 0);
            pCard->AddElementToField(serialNo, 1);
            pCard->AddElementToField(hetID, 2);
            pCard->AddElementToField(num, 5);
       }
}

void _MAXIT::insert_het_card(_CHAIN *chain, const char *resname,
       const int resnum, const char *ins_code, const int atmnum)
{
       if (!strcmp(resname, "DUM") || !strcmp(resname, "UNK") ||
           !strcmp(resname, "N") /* || !strcmp(resname, "UNX") ||
           !strcmp(resname, "UNL") */ ) return;
       _FIELD* initRes = chain->find_pdb_Residue(resname, resnum, ins_code);
       if (initRes) {
            int FstIndex = chain->index();
            int FstRes = initRes->ResIndex;
            Insert_a_Het(FstIndex, FstRes, atmnum);
       }
}

void _MAXIT::ndb_add_missing_information(const char *TokenName, const int TokenField,
                    const char *TokenValue)
{
       int TokenId = find_card_id(TokenName);
       int FieldId = TokenField - 1;
       if (TokenId < 0 || FieldId < 1) return;

       One_Card_Info_Format *pCard = _CardInfo[TokenId].Head();
       if (pCard) {
            while (pCard) {
                 pCard->AddElementToField(TokenValue, FieldId);
                 pCard = pCard->Next();
            }
       } else {
            pCard = _CardInfo[TokenId].add_a_new_card(NdbToken[TokenId], 0);
            pCard->AddElementToField(TokenValue, FieldId);
       }
}

void _MAXIT::write_restraints_correspondences(FILE *outfile)
{
       int i, serial_no = 1, token_ids[8], numRemark = 0;
       char atom_name[5];
       _ATOM *atom = NULL;
       _RESIDUE *residue = NULL;
       _CHAIN *chain = NULL;
       One_Card_Info_Format *pCard = NULL, *qCard = NULL;

       token_ids[0] = HEADER_TOKEN-1; token_ids[1] = find_card_id("TITLE");
       token_ids[2] = COMPND_TOKEN-1; token_ids[3] = SOURCE_TOKEN-1;
       token_ids[4] = find_card_id("KEYWDS"); token_ids[5] = EXPDTA_TOKEN-1;
       token_ids[6] = AUTHOR_TOKEN-1; token_ids[7] = REVDAT_TOKEN-1;

       pCard = _CardInfo[HEADER_TOKEN-1].Head();
       qCard = _CardInfo[PDBFIL_TOKEN-1].Head();
       if (pCard && qCard) strcpy(pCard->Field(3), qCard->Field(1));

       replace_pdb_compnd_source_card();
       update_tokens(NDB_FILE_FORMAT_PDB);
       for (i = 0; i < 8; i++) {
            print_general_cards(outfile, token_ids[i], NDB_FILE_FORMAT_PDB, numRemark, 1);
       }
       fprintf(outfile, "\n");

       fprintf(outfile, "  Entry H atom name         Submitted Coord H atom name\n");
       for (i = 0; i < _num_mol; i++) {
            if (_num_mol > 1) fprintf(outfile, "  Start of MODEL %4d\n", i + 1);
            serial_no = 1;
            chain = _molecules[i]->GetFirstChain();
            while (chain) {
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      atom = residue->GetFirstAtom();
                      while (atom) {
                           if (!atom->is_hydrogen()) {
                                atom = residue->GetNextAtom();
                                continue;
                           }
                           atom_name[0] = '\0';
                           print_atom_atomName_field(atom->atom_type(), 0, atom_name,
                               atom->pdb_atmnam(), atom->pdb_resnam(), atom->align()); 
                           fprintf(outfile, "%5d   %4s  ", serial_no++, atom_name);
                           fprintf(outfile, "%3s%4d          ",
                              atom->pdb_resnam(), atom->pdb_resnum());
                           print_atom_atomName_field(atom->atom_type(), 0, atom_name,
                               atom->pub_atmnam(), atom->pub_resnam(), atom->align());
                           fprintf(outfile, "%4s      %3s%4d%8.3f%8.3f%8.3f\n",
                              atom_name, atom->pub_resnam(), atom->pub_resnum(),
                              atom->orig().x, atom->orig().y, atom->orig().z);
                           atom = residue->GetNextAtom();
                      }
                      residue = chain->GetNextResidue();
                 }
                 chain = _molecules[i]->GetNextChain();
            }
       }
}

void _MAXIT::switch_hydrogen_labeling()
{
       int l;
       _CHAIN *chain = NULL;
       _RESIDUE *residue = NULL;

       for (l = 0; l < _num_mol; l++) {
            chain = _molecules[l]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() != ATOMP_TOKEN) {
                      chain = _molecules[l]->GetNextChain();
                      continue;
                 }
                 chain->labeling_iupac_hydrogen();
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      residue->labeling_iupac_hydrogen();
                      residue = chain->GetNextResidue();
                 }
                 chain = _molecules[l]->GetNextChain();
            }
       }
}

void _MAXIT::ndb_write_hydrogen_index()
{
       char filename[100];
       _ATOM *atom = NULL;
       _RESIDUE *residue = NULL;
       _CHAIN *chain = NULL;

       sprintf(filename, "%s.index", _input_filename);
       FILE *outfile = fopen(filename, "w");
       if (_num_mol) { 
            chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      atom = residue->GetFirstAtom();
                      while (atom) {
                           if (!atom->is_hydrogen()) {
                                atom = residue->GetNextAtom();
                                continue;
                           }
                           if (strcmp(atom->pdb_atmnam(), atom->pub_atmnam())) {
                                fprintf(outfile, "%s %s %d %s\n",
                                      atom->pub_atmnam(), atom->pdb_resnam(),
                                      atom->pdb_resnum(), atom->pdb_atmnam());
                           }
                           atom = residue->GetNextAtom();
                      }
                 }
                 chain = _molecules[0]->GetNextChain();
            }
       }
       fclose (outfile);
}

void _MAXIT::add_modification_information()
{
       int i, is_sugar = 0, exist_unknown = 0;
       char PDB_ID[15], comments[40];
       _FIELD *Fst = NULL, *Snd = NULL;
       _CHAIN *ch1 = NULL, *ch2 = NULL;

       if (_num_mol == 0) return;

       PDB_ID[0] = '\0';
       One_Card_Info_Format *pCard = _CardInfo[PDBFIL_TOKEN-1].Head();
       if (pCard) strcpy(PDB_ID, pCard->Field(1));

       _MOLECULE *mol = _molecules[0];
       for (i = 0; i < _n_link; i++) {
            if (strcmp(_link[i]->SymOP_1, "") ||
                strcmp(_link[i]->SymOP_2, "")) continue;

            ch1 = mol->GetIndexChain(_link[i]->FstIndex);
            ch2 = mol->GetIndexChain(_link[i]->SndIndex);
            if (!ch1 || !ch2 || ch1 == ch2) continue;
            Fst = ch1->SeqResIndex(_link[i]->FstRes);
            Snd = ch2->SeqResIndex(_link[i]->SndRes);
            if (!Fst || !Snd) continue;

            comments[0] = '\0';
            if (_link[i]->details && strlen(_link[i]->details) < 40)
                 strcpy(comments, _link[i]->details);
            if (ch1->chain_type() == ATOMN_TOKEN &&
                ch2->chain_type() != ATOMP_TOKEN &&
                ch2->chain_type() != ATOMN_TOKEN) {
                 if (strcmp(comments, ""))
                      Insert_a_Modres(PDB_ID, _link[i]->FstIndex, _link[i]->FstRes,
                                       Fst->Field[0], comments);
/*
                 if (!strcmp(_link[i]->FstAtom, "P") ||
                     !strcmp(_link[i]->FstAtom, "O1P") ||
                     !strcmp(_link[i]->FstAtom, "O2P"))
                       Insert_a_Modifp(Snd->Field[0], _link[i]->SndAtom,
                             _link[i]->SndIndex, _link[i]->SndRes,
                             _link[i]->FstAtom, _link[i]->FstIndex,
                             _link[i]->FstRes, "");
                 else if (_link[i]->FstAtom[2] == '*')
                       Insert_a_Modifs(Snd->Field[0], _link[i]->SndAtom,
                             _link[i]->SndIndex, _link[i]->SndRes,
                             _link[i]->FstAtom, _link[i]->FstIndex,
                             _link[i]->FstRes, "", "");
                 else Insert_a_Modifb(Snd->Field[0], _link[i]->SndAtom,
                             _link[i]->SndIndex, _link[i]->SndRes,
                             _link[i]->FstAtom, _link[i]->FstIndex,
                             _link[i]->FstRes, "");
*/
            } else if (ch2->chain_type() == ATOMN_TOKEN &&
                       ch1->chain_type() != ATOMP_TOKEN &&
                       ch1->chain_type() != ATOMN_TOKEN) {
                 if (strcmp(comments, ""))
                      Insert_a_Modres(PDB_ID, _link[i]->SndIndex, _link[i]->SndRes,
                            Snd->Field[0], comments);
/*
                 if (!strcmp(_link[i]->SndAtom, "P") ||
                     !strcmp(_link[i]->SndAtom, "O1P") ||
                     !strcmp(_link[i]->SndAtom, "O2P"))
                       Insert_a_Modifp(Fst->Field[0], _link[i]->FstAtom,
                             _link[i]->FstIndex, _link[i]->FstRes,
                             _link[i]->SndAtom, _link[i]->SndIndex,
                             _link[i]->SndRes, "");
                 else if (_link[i]->SndAtom[2] == '*')
                       Insert_a_Modifs(Fst->Field[0], _link[i]->FstAtom,
                             _link[i]->FstIndex, _link[i]->FstRes,
                             _link[i]->SndAtom, _link[i]->SndIndex,
                             _link[i]->SndRes, "", "");
                 else Insert_a_Modifb(Fst->Field[0], _link[i]->FstAtom,
                             _link[i]->FstIndex, _link[i]->FstRes,
                             _link[i]->SndAtom, _link[i]->SndIndex,
                             _link[i]->SndRes, "");
*/
            } else if (ch1->chain_type() == ATOMP_TOKEN &&
                       ch2->chain_type() != ATOMP_TOKEN &&
                       ch2->chain_type() != ATOMN_TOKEN) {
                 if (is_a_standard_residue(Fst->Field[0])) {
                      if (!strcmp(comments, "")) { 
                           is_sugar = 0;
                           if (find_residue_type(Snd->Field[0], exist_unknown) == ATOMS_TOKEN)
                                is_sugar = 1;
                           if ((!strcmp(Fst->Field[0], "ASN") ||
                                !strcmp(Fst->Field[0], "SER") ||
                                !strcmp(Fst->Field[0], "THR")) && is_sugar)
                                strcpy(comments, "GLYCOSYLATION SITE");
                      }
                      if (strcmp(comments, ""))
                           Insert_a_Modres(PDB_ID, _link[i]->FstIndex,
                                   _link[i]->FstRes, Fst->Field[0], comments);
                 }
            } else if (ch2->chain_type() == ATOMP_TOKEN &&
                       ch1->chain_type() != ATOMP_TOKEN &&
                       ch1->chain_type() != ATOMN_TOKEN) {
                 if (is_a_standard_residue(Snd->Field[0])) {
                      if (!strcmp(comments, "")) { 
                           is_sugar = 0;
                           if (find_residue_type(Fst->Field[0], exist_unknown) == ATOMS_TOKEN)
                                is_sugar = 1;
                           if ((!strcmp(Snd->Field[0], "ASN") ||
                                !strcmp(Snd->Field[0], "SER") ||
                                !strcmp(Snd->Field[0], "THR")) && is_sugar)
                                strcpy(comments, "GLYCOSYLATION SITE");
                      }
                      if (strcmp(comments, ""))
                           Insert_a_Modres(PDB_ID, _link[i]->SndIndex,
                                   _link[i]->SndRes, Snd->Field[0], comments);
                 }
            }
       }
}

void _MAXIT::get_virus_matrix_from_remark_350(const int add_identical)
{
       int id, id_index, text_idex, rowNo, row_no, irow, num;
       char buffer[50], text[100], m1[10], m2[10], m3[10], v[15], *p, *q;
       CifString string;
       ISTable *Table = NULL, *Table1 = NULL;

       Table = _CifObj->GetTablePtr(_StructureId, "struct_ncs_oper");
       if (!is_empty_table(Table)) return;
       Table1 = _CifObj->GetTablePtr(_StructureId, "database_PDB_remark");
       if (is_empty_table(Table1)) return;
       id_index = Table1->GetColumnIndex("id");
       text_idex = Table1->GetColumnIndex("text");
       if (id_index < 0 || text_idex < 0) return;

       if (_CifObj->IsTablePresent(_StructureId, "struct_ncs_oper"))
            _CifObj->DeleteTable(_StructureId, "struct_ncs_oper");

       Table = new ISTable("struct_ncs_oper");
       Table->AddColumn("id");
       Table->AddColumn("code");
       Table->AddColumn("details");
       Table->AddColumn("matrix[1][1]");
       Table->AddColumn("matrix[1][2]");
       Table->AddColumn("matrix[1][3]");
       Table->AddColumn("matrix[2][1]");
       Table->AddColumn("matrix[2][2]");
       Table->AddColumn("matrix[2][3]");
       Table->AddColumn("matrix[3][1]");
       Table->AddColumn("matrix[3][2]");
       Table->AddColumn("matrix[3][3]");
       Table->AddColumn("vector[1]");
       Table->AddColumn("vector[2]");
       Table->AddColumn("vector[3]");

       rowNo = Table1->GetNumRows();
       row_no = -1;

       if (add_identical) {
            row_no++;
            Table->AddRow();
            string.Copy("1");
            Table->UpdateCell(string, 0, row_no);
            string.Copy("given");
            Table->UpdateCell(string, 1, row_no);

            string.Copy("1.000000");
            Table->UpdateCell(string, 3, row_no);
            Table->UpdateCell(string, 7, row_no);
            Table->UpdateCell(string, 11, row_no);

            string.Copy("0.000000");
            Table->UpdateCell(string, 4, row_no);
            Table->UpdateCell(string, 5, row_no);
            Table->UpdateCell(string, 6, row_no);
            Table->UpdateCell(string, 8, row_no);
            Table->UpdateCell(string, 9, row_no);
            Table->UpdateCell(string, 10, row_no);

            string.Copy("0.00000");
            Table->UpdateCell(string, 12, row_no);
            Table->UpdateCell(string, 13, row_no);
            Table->UpdateCell(string, 14, row_no);
       }

       for (irow = 0; irow < rowNo; irow++) {
            Table1->GetCell(string, id_index, irow);
            if (string != "350") continue;

            Table1->GetCell(string,  text_idex, irow);
            q = string.Text();
            while (q) {
                 q = get_line_from_string(text, q);
                 if (!strcmp(text, "")) continue;

                 p = get_word(text, buffer);
                 if (strncmp(buffer, "BIOMT", 5) || strlen(buffer) < 6) continue;

                 num = atoi(&buffer[5]);
                 if (!p) continue;
                 p = get_word(p, buffer);
                 id = atoi(buffer);
                 strcpy(v, "0.00000");
                 if (!p) continue; p = get_word(p, m1);
                 if (!p) continue; p = get_word(p, m2);
                 if (!p) continue; p = get_word(p, m3);
                 if (p) p = get_word(p, v);

                 if (num == 1) { row_no++; Table->AddRow(); }
                 if (row_no < 0) continue;
     
                 if (num == 1) {
                      if (add_identical) 
                           sprintf(buffer, "%d", id + 1);
                      else sprintf(buffer, "%d", id);
                      string.Copy(buffer);
                      Table->UpdateCell(string, 0, row_no);

                      if (!add_identical && id == 1)
                           string.Copy("given");
                      else string.Copy("generate");
                      Table->UpdateCell(string, 1, row_no);

                      string.Copy(m1);
                      Table->UpdateCell(string, 3, row_no);
                      string.Copy(m2);
                      Table->UpdateCell(string, 4, row_no);
                      string.Copy(m3);
                      Table->UpdateCell(string, 5, row_no);
                      string.Copy(v);
                      Table->UpdateCell(string, 12, row_no);
                 } else if (num == 2) {
                      string.Copy(m1);
                      Table->UpdateCell(string, 6, row_no);
                      string.Copy(m2);
                      Table->UpdateCell(string, 7, row_no);
                      string.Copy(m3);
                      Table->UpdateCell(string, 8, row_no);
                      string.Copy(v);
                      Table->UpdateCell(string, 13, row_no);
                 } else if (num == 3) {
                      string.Copy(m1);
                      Table->UpdateCell(string, 9, row_no);
                      string.Copy(m2);
                      Table->UpdateCell(string, 10, row_no);
                      string.Copy(m3);
                      Table->UpdateCell(string, 11, row_no);
                      string.Copy(v);
                      Table->UpdateCell(string, 14, row_no);
                 }
            }
            break;
       }
       _CifObj->WriteTable(Table, _StructureId, "struct_ncs_oper");
}

void _MAXIT::classification(FILE *summary, FILE *summary1, char *het)
{
       int is_p = 0, is_d = 0, is_r = 0, is_h = 0, is_e = 0, is_c = 0;
       int is_g = 0, is_enzyme = 0;
       _CHAIN *chain = NULL;
       One_Card_Info_Format *pCard = NULL;

       if (_num_mol == 0) return;

       het[0] = '\0';
       pCard = _CardInfo[HET_TOKEN-1].Head();
       while (pCard) {
            if (strcmp(pCard->Field(1), "HOH") &&
                strcmp(pCard->Field(1), "DOD") &&
                strcmp(pCard->Field(1), "HN2") &&
                strcmp(pCard->Field(1), "CH2") &&
                strcmp(pCard->Field(1), "ACE")) {
                 if (!strcmp(het, "")) strcpy(het, pCard->Field(1));
                 else {
                      strcat(het, " ");
                      strcat(het, pCard->Field(1));
                 }
            }
            pCard = pCard->Next();
       }

       chain = _molecules[0]->GetFirstChain();
       while (chain) {
            if (chain->chain_type() != ATOMN_TOKEN &&
                chain->chain_type() != ATOMP_TOKEN &&
                chain->chain_type() != ATOMS_TOKEN) {
                 chain = _molecules[0]->GetNextChain();
                 continue;
            }

            if (chain->chain_type() == ATOMS_TOKEN &&
               (chain->is_linked() || chain->ResidueNumbers() < 3)) {
                 chain = _molecules[0]->GetNextChain();
                 continue;
            }

            fprintf(summary, "%s\t", _CardInfo[PDBFIL_TOKEN-1].Head()->Field(1));
            if (strcmp(chain->PDB_ChainID(), ""))
                 fprintf(summary, "%s\t", chain->PDB_ChainID());
            else fprintf(summary, "\t");
            fprintf(summary, "%s\t%d\t", chain->ChainID(),
                  chain->entity_id());

            pCard = _CardInfo[ECNUM_TOKEN-1].Head();
            while (pCard) {
                 if (atoi(pCard->Field(1)) == chain->entity_id())
                      break;
                 pCard = pCard->Next();
            }
            is_enzyme = 0;
            if (pCard && strcmp(pCard->Field(2), "")) {
                 fprintf(summary, "%s", pCard->Field(2));
                 is_enzyme = 1;
            }
            fprintf(summary, "\t");

            if (chain->chain_type() == ATOMN_TOKEN) {
                 if (chain->na_type() == ATOMN_TYPE_DNA_ONLY) {
                      fprintf(summary, "DNA");
                      is_d = 1;
                 } else if (chain->na_type() == ATOMN_TYPE_DNA_RNA) {
                      fprintf(summary, "HYBRID");
                      is_h = 1;
                 } else if (chain->na_type() & ATOMN_TYPE_RNA_ONLY) {
                      fprintf(summary, "RNA");
                      is_r = 1;
                 } else if (chain->na_type() < 0) {
                      pCard = _CardInfo[ENTDES_TOKEN-1].Head();
                      while (pCard) {
                           if (atoi(pCard->Field(1)) == chain->entity_id())
                                break;
                           pCard = pCard->Next();
                      }
                      if (pCard && (strstr(pCard->Field(3), "RIBOSOM") ||
                          strstr(pCard->Field(3), "ribiosom") ||
                          strstr(pCard->Field(3), "RNA") ||
                          strstr(pCard->Field(3), "rna")))
                           fprintf(summary, "RNA");
                      else fprintf(summary, "DNA");
                 }
            } else if (chain->chain_type() == ATOMP_TOKEN)
                 fprintf(summary, "PROTEIN");
            fprintf(summary, "\t");

            pCard = NULL;
            if (chain->chain_type() == ATOMP_TOKEN ||
                chain->chain_type() == ATOMN_TOKEN) {
                 pCard = _CardInfo[DBREF_TOKEN-1].Head();
                 while (pCard) {
                      if (!strcmp(chain->PDB_ChainID(), pCard->Field(2)))
                           break;
                      pCard = pCard->Next();
                 }
            }
            if (pCard && strcmp(pCard->Field(8), ""))
                 fprintf(summary, "%s", pCard->Field(8));
            fprintf(summary, "\t");

            if (pCard && strcmp(pCard->Field(7), ""))
                 fprintf(summary, "%s", pCard->Field(7));
            fprintf(summary, "\t");

            if (chain->chain_type() == ATOMP_TOKEN) {
                 fprintf(summary, "Y\t");
                 is_p = 1;
            } else fprintf(summary, "N\t");

            if (chain->chain_type() == ATOMN_TOKEN)
                 fprintf(summary, "Y\t");
            else fprintf(summary, "N\t");

            if (chain->chain_type() == ATOMP_TOKEN && is_enzyme) {
                 fprintf(summary, "Y\t");
                 is_e = 1;
            } else fprintf(summary, "N\t");

            if (chain->chain_type() == ATOMS_TOKEN) {
                 fprintf(summary, "Y\t");
                 is_c = 1;
            } else fprintf(summary, "N\t");

            if (chain->chain_type() == ATOMP_TOKEN &&
                chain->is_linked()) {
                 fprintf(summary, "Y\n");
                 is_g = 1;
            } else fprintf(summary, "N\n");
            chain = _molecules[0]->GetNextChain();
       }

       fprintf(summary1, "%s\t", _CardInfo[PDBFIL_TOKEN-1].Head()->Field(1));
       if (is_p) fprintf(summary1, "Y\t"); else fprintf(summary1, "N\t");

       if (is_d || is_h) fprintf(summary1, "Y\t"); else fprintf(summary1, "N\t");

       if (is_r || is_h) fprintf(summary1, "Y\t"); else fprintf(summary1, "N\t");

       if (is_h) fprintf(summary1, "Y\t"); else fprintf(summary1, "N\t");

       if (is_e) fprintf(summary1, "Y\t"); else fprintf(summary1, "N\t");

       if (is_c) fprintf(summary1, "Y\t"); else fprintf(summary1, "N\t");

       if (is_g) fprintf(summary1, "Y\n"); else fprintf(summary1, "N\n");
}

void _MAXIT::delete_na_structural_features()
{
       if (!_CifObj) return;

       if (_CifObj->IsTablePresent(_StructureId, "ndb_struct_conf_na"))
            _CifObj->DeleteTable(_StructureId, "ndb_struct_conf_na");

       if (_CifObj->IsTablePresent(_StructureId, "ndb_struct_na_base_pair"))
            _CifObj->DeleteTable(_StructureId, "ndb_struct_na_base_pair");

       if (_CifObj->IsTablePresent(_StructureId, "ndb_struct_na_base_pair_step"))
            _CifObj->DeleteTable(_StructureId, "ndb_struct_na_base_pair_step");

       if (_CifObj->IsTablePresent(_StructureId, "ndb_entity_poly_na_type"))
            _CifObj->DeleteTable(_StructureId, "ndb_entity_poly_na_type");

       if (_CifObj->IsTablePresent(_StructureId, "ndb_entity_poly_na_nonstandard"))
            _CifObj->DeleteTable(_StructureId, "ndb_entity_poly_na_nonstandard");
}

void _MAXIT::update_na_structural_features()
{
       int i, entity_id[100], n_entity_id = 0, irow = 0, j = 0, e_idx, f_idx;
       char buff[50], num1[10], num2[10], num3[10], num4[10];
       CifString string, string1, string2;
       _CHAIN *ch1 = NULL, *ch2 = NULL, *chain = NULL;
       ConnectFormat *drug = NULL;
       _RESIDUE *residue = NULL;
       ISTable *Table = NULL, *Table1 = NULL;

       if (_num_mol == 0 || _molecules == NULL) return;
       if (!_CifObj) return;

       na_classification();

       Table = _CifObj->GetTablePtr(_StructureId, "ndb_struct_na_base_pair");
       if (is_empty_table(Table) && _n_bb_params) {
            if (_CifObj->IsTablePresent(_StructureId, "ndb_struct_na_base_pair"))
                 _CifObj->DeleteTable(_StructureId, "ndb_struct_na_base_pair");
            Table = new ISTable("ndb_struct_na_base_pair");
            Table->AddColumn("model_number");
            Table->AddColumn("i_label_asym_id");
            Table->AddColumn("i_label_comp_id");
            Table->AddColumn("i_label_seq_id");
            Table->AddColumn("i_symmetry");
            Table->AddColumn("j_label_asym_id");
            Table->AddColumn("j_label_comp_id");
            Table->AddColumn("j_label_seq_id");
            Table->AddColumn("j_symmetry");
            Table->AddColumn("shear");
            Table->AddColumn("stretch");
            Table->AddColumn("stagger");
            Table->AddColumn("buckle");
            Table->AddColumn("propel");
            Table->AddColumn("opening");
            Table->AddColumn("pair_number");
            Table->AddColumn("pair_name");
            Table->AddColumn("i_auth_asym_id");
            Table->AddColumn("i_auth_seq_id");
            Table->AddColumn("i_PDB_ins_code");
            Table->AddColumn("j_auth_asym_id");
            Table->AddColumn("j_auth_seq_id");
            Table->AddColumn("j_PDB_ins_code");
            Table->AddColumn("hbond_type_28");
            Table->AddColumn("hbond_type_12");
            _CifObj->WriteTable(Table, _StructureId, "ndb_struct_na_base_pair");
            for (i = 0; i < _n_bb_params; i++) {
                 Table->AddRow();
                 string.Copy("1");
                 Table->UpdateCell(string, 0, i);
                 string.Copy(_bb_params[i]->ChnID_I);
                 Table->UpdateCell(string, 1, i);
                 string.Copy(_bb_params[i]->Base_I);
                 Table->UpdateCell(string, 2, i);
                 sprintf(buff, "%d", _bb_params[i]->ResNum_I);
                 string.Copy(buff);
                 Table->UpdateCell(string, 3, i);
                 string.Copy(_bb_params[i]->Sym_I);
                 Table->UpdateCell(string, 4, i);
                 string.Copy(_bb_params[i]->ChnID_J);
                 Table->UpdateCell(string, 5, i);
                 string.Copy(_bb_params[i]->Base_J);
                 Table->UpdateCell(string, 6, i);
                 sprintf(buff, "%d", _bb_params[i]->ResNum_J);
                 string.Copy(buff);
                 Table->UpdateCell(string, 7, i);
                 string.Copy(_bb_params[i]->Sym_J);
                 Table->UpdateCell(string, 8, i);
                 sprintf(buff, "%.3f", _bb_params[i]->Shear);
                 string.Copy(buff);
                 Table->UpdateCell(string, 9, i);
                 sprintf(buff, "%.3f", _bb_params[i]->Stretch);
                 string.Copy(buff);
                 Table->UpdateCell(string, 10, i);
                 sprintf(buff, "%.3f", _bb_params[i]->Stagger);
                 string.Copy(buff);
                 Table->UpdateCell(string, 11, i);
                 sprintf(buff, "%.3f", _bb_params[i]->Buckle);
                 string.Copy(buff);
                 Table->UpdateCell(string, 12, i);
                 sprintf(buff, "%.3f", _bb_params[i]->Propel);
                 string.Copy(buff);
                 Table->UpdateCell(string, 13, i);
                 sprintf(buff, "%.3f", _bb_params[i]->Opening);
                 string.Copy(buff);
                 Table->UpdateCell(string, 14, i);
                 sprintf(buff, "%d", i+1);
                 string.Copy(buff);
                 Table->UpdateCell(string, 15, i);

                 if (strcmp(_bb_params[i]->InsCode_I, ""))
                      sprintf(num1, "%d%s", _bb_params[i]->ResNum_I_PDB,
                                              _bb_params[i]->InsCode_I);
                 else sprintf(num1, "%d", _bb_params[i]->ResNum_I_PDB);
                 if (strcmp(_bb_params[i]->InsCode_J, ""))
                      sprintf(num2, "%d%s", _bb_params[i]->ResNum_J_PDB,
                                              _bb_params[i]->InsCode_J);
                 else sprintf(num2, "%d", _bb_params[i]->ResNum_J_PDB);

                 sprintf(buff, "%s_%s%s:%s%s_%s", _bb_params[i]->ChnID_I_PDB,
                      _bb_params[i]->Base_I, num1, _bb_params[i]->Base_J,
                      num2, _bb_params[i]->ChnID_J_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 16, i);

                 string.Copy(_bb_params[i]->ChnID_I_PDB);
                 Table->UpdateCell(string, 17, i);
                 sprintf(buff, "%d", _bb_params[i]->ResNum_I_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 18, i);
                 string.Copy(_bb_params[i]->InsCode_I);
                 Table->UpdateCell(string, 19, i);
                 string.Copy(_bb_params[i]->ChnID_J_PDB);
                 Table->UpdateCell(string, 20, i);
                 sprintf(buff, "%d", _bb_params[i]->ResNum_J_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 21, i);
                 string.Copy(_bb_params[i]->InsCode_J);
                 Table->UpdateCell(string, 22, i);
                 string.Copy(_bb_params[i]->_saenger_classification);
                 Table->UpdateCell(string, 23, i);
                 string.Copy(_bb_params[i]->_leontis_westhof_classification);
                 Table->UpdateCell(string, 24, i);
            }
       }

       Table = _CifObj->GetTablePtr(_StructureId, "ndb_struct_na_base_pair_step");
       if (is_empty_table(Table) && _n_ib_params) {
            if (_CifObj->IsTablePresent(_StructureId, "ndb_struct_na_base_pair_step"))
                 _CifObj->DeleteTable(_StructureId, "ndb_struct_na_base_pair_step");
            Table = new ISTable("ndb_struct_na_base_pair_step");
            Table->AddColumn("model_number");
            Table->AddColumn("i_label_asym_id_1");
            Table->AddColumn("i_label_comp_id_1");
            Table->AddColumn("i_label_seq_id_1");
            Table->AddColumn("i_symmetry_1");
            Table->AddColumn("j_label_asym_id_1");
            Table->AddColumn("j_label_comp_id_1");
            Table->AddColumn("j_label_seq_id_1");
            Table->AddColumn("j_symmetry_1");
            Table->AddColumn("i_label_asym_id_2");
            Table->AddColumn("i_label_comp_id_2");
            Table->AddColumn("i_label_seq_id_2");
            Table->AddColumn("i_symmetry_2");
            Table->AddColumn("j_label_asym_id_2");
            Table->AddColumn("j_label_comp_id_2");
            Table->AddColumn("j_label_seq_id_2");
            Table->AddColumn("j_symmetry_2");
            Table->AddColumn("shift");
            Table->AddColumn("slide");
            Table->AddColumn("rise");
            Table->AddColumn("tilt");
            Table->AddColumn("roll");
            Table->AddColumn("twist");
            Table->AddColumn("x_displacement");
            Table->AddColumn("y_displacement");
            Table->AddColumn("helical_rise");
            Table->AddColumn("inclination");
            Table->AddColumn("tip");
            Table->AddColumn("helical_twist");
            Table->AddColumn("step_number");
            Table->AddColumn("step_name");
            Table->AddColumn("i_auth_asym_id_1");
            Table->AddColumn("i_auth_seq_id_1");
            Table->AddColumn("i_PDB_ins_code_1");
            Table->AddColumn("j_auth_asym_id_1");
            Table->AddColumn("j_auth_seq_id_1");
            Table->AddColumn("j_PDB_ins_code_1");
            Table->AddColumn("i_auth_asym_id_2");
            Table->AddColumn("i_auth_seq_id_2");
            Table->AddColumn("i_PDB_ins_code_2");
            Table->AddColumn("j_auth_asym_id_2");
            Table->AddColumn("j_auth_seq_id_2");
            Table->AddColumn("j_PDB_ins_code_2");
            _CifObj->WriteTable(Table, _StructureId, "ndb_struct_na_base_pair_step");
            for (i = 0; i < _n_ib_params; i++) {
                 Table->AddRow();
                 string.Copy("1");
                 Table->UpdateCell(string, 0, i);
                 string.Copy(_ib_params[i]->ChnID_I1);
                 Table->UpdateCell(string, 1, i);
                 string.Copy(_ib_params[i]->Base_I1);
                 Table->UpdateCell(string, 2, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_I1);
                 string.Copy(buff);
                 Table->UpdateCell(string, 3, i);
                 string.Copy(_ib_params[i]->Sym_I1);
                 Table->UpdateCell(string, 4, i);
                 string.Copy(_ib_params[i]->ChnID_J1);
                 Table->UpdateCell(string, 5, i);
                 string.Copy(_ib_params[i]->Base_J1);
                 Table->UpdateCell(string, 6, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_J1);
                 string.Copy(buff);
                 Table->UpdateCell(string, 7, i);
                 string.Copy(_ib_params[i]->Sym_J1);
                 Table->UpdateCell(string, 8, i);
                 string.Copy(_ib_params[i]->ChnID_I2);
                 Table->UpdateCell(string, 9, i);
                 string.Copy(_ib_params[i]->Base_I2);
                 Table->UpdateCell(string, 10, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_I2);
                 string.Copy(buff);
                 Table->UpdateCell(string, 11, i);
                 string.Copy(_ib_params[i]->Sym_I2);
                 Table->UpdateCell(string, 12, i);
                 string.Copy(_ib_params[i]->ChnID_J2);
                 Table->UpdateCell(string, 13, i);
                 string.Copy(_ib_params[i]->Base_J2);
                 Table->UpdateCell(string, 14, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_J2);
                 string.Copy(buff);
                 Table->UpdateCell(string, 15, i);
                 string.Copy(_ib_params[i]->Sym_J2);
                 Table->UpdateCell(string, 16, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Shift);
                 string.Copy(buff);
                 Table->UpdateCell(string, 17, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Slide);
                 string.Copy(buff);
                 Table->UpdateCell(string, 18, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Rise);
                 string.Copy(buff);
                 Table->UpdateCell(string, 19, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Tilt);
                 string.Copy(buff);
                 Table->UpdateCell(string, 20, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Roll);
                 string.Copy(buff);
                 Table->UpdateCell(string, 21, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Twist);
                 string.Copy(buff);
                 Table->UpdateCell(string, 22, i);
                 sprintf(buff, "%.3f", _ib_params[i]->X_disp);
                 string.Copy(buff);
                 Table->UpdateCell(string, 23, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Y_disp);
                 string.Copy(buff);
                 Table->UpdateCell(string, 24, i);
                 sprintf(buff, "%.3f", _ib_params[i]->H_rise);
                 string.Copy(buff);
                 Table->UpdateCell(string, 25, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Incl);
                 string.Copy(buff);
                 Table->UpdateCell(string, 26, i);
                 sprintf(buff, "%.3f", _ib_params[i]->Tip);
                 string.Copy(buff);
                 Table->UpdateCell(string, 27, i);
                 sprintf(buff, "%.3f", _ib_params[i]->H_twist);
                 string.Copy(buff);
                 Table->UpdateCell(string, 28, i);
                 sprintf(buff, "%d", i+1);
                 string.Copy(buff);
                 Table->UpdateCell(string, 29, i);

                 if (strcmp(_ib_params[i]->InsCode_I1, ""))
                      sprintf(num1, "%d%s", _ib_params[i]->ResNum_I1_PDB,
                                              _ib_params[i]->InsCode_I1);
                 else sprintf(num1, "%d", _ib_params[i]->ResNum_I1_PDB);
                 if (strcmp(_ib_params[i]->InsCode_J1, ""))
                      sprintf(num2, "%d%s", _ib_params[i]->ResNum_J1_PDB,
                                              _ib_params[i]->InsCode_J1);
                 else sprintf(num2, "%d", _ib_params[i]->ResNum_J1_PDB);
                 if (strcmp(_ib_params[i]->InsCode_I2, ""))
                      sprintf(num3, "%d%s", _ib_params[i]->ResNum_I2_PDB,
                                              _ib_params[i]->InsCode_I2);
                 else sprintf(num3, "%d", _ib_params[i]->ResNum_I2_PDB);
                 if (strcmp(_ib_params[i]->InsCode_J2, ""))
                      sprintf(num4, "%d%s", _ib_params[i]->ResNum_J2_PDB,
                                              _ib_params[i]->InsCode_J2);
                 else sprintf(num4, "%d", _ib_params[i]->ResNum_J2_PDB);

                 sprintf(buff, "%s%s_%s%s%s%s:%s%s%s%s_%s%s",
                      _ib_params[i]->ChnID_I1_PDB, _ib_params[i]->ChnID_I2_PDB,
                      _ib_params[i]->Base_I1, num1, _ib_params[i]->Base_I2, num3,
                      _ib_params[i]->Base_J2, num4, _ib_params[i]->Base_J1, num2,
                      _ib_params[i]->ChnID_J2_PDB, _ib_params[i]->ChnID_J1_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 30, i);

                 string.Copy(_ib_params[i]->ChnID_I1_PDB);
                 Table->UpdateCell(string, 31, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_I1_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 32, i);
                 string.Copy(_ib_params[i]->InsCode_I1);
                 Table->UpdateCell(string, 33, i);
                 string.Copy(_ib_params[i]->ChnID_J1_PDB);
                 Table->UpdateCell(string, 34, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_J1_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 35, i);
                 string.Copy(_ib_params[i]->InsCode_J1);
                 Table->UpdateCell(string, 36, i);
                 string.Copy(_ib_params[i]->ChnID_I2_PDB);
                 Table->UpdateCell(string, 37, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_I2_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 38, i);
                 string.Copy(_ib_params[i]->InsCode_I2);
                 Table->UpdateCell(string, 39, i);
                 string.Copy(_ib_params[i]->ChnID_J2_PDB);
                 Table->UpdateCell(string, 40, i);
                 sprintf(buff, "%d", _ib_params[i]->ResNum_J2_PDB);
                 string.Copy(buff);
                 Table->UpdateCell(string, 41, i);
                 string.Copy(_ib_params[i]->InsCode_J2);
                 Table->UpdateCell(string, 42, i);
            }
       }

       Table = _CifObj->GetTablePtr(_StructureId, "ndb_entity_poly_na_type");
       if (is_empty_table(Table)) {
            if (_CifObj->IsTablePresent(_StructureId, "ndb_entity_poly_na_type"))
                 _CifObj->DeleteTable(_StructureId, "ndb_entity_poly_na_type");
            Table = new ISTable("ndb_entity_poly_na_type");
            Table->AddColumn("entity_id");
            Table->AddColumn("type");
            _CifObj->WriteTable(Table, _StructureId, "ndb_entity_poly_na_type");
       } else Table = NULL;

       Table1 = _CifObj->GetTablePtr(_StructureId, "ndb_entity_poly_na_nonstandard");
       if (is_empty_table(Table1)) {
            if (_CifObj->IsTablePresent(_StructureId, "ndb_entity_poly_na_nonstandard"))
                 _CifObj->DeleteTable(_StructureId, "ndb_entity_poly_na_nonstandard");
            Table1 = new ISTable("ndb_entity_poly_na_nonstandard");
            Table1->AddColumn("entity_id");
            Table1->AddColumn("feature");
            _CifObj->WriteTable(Table1, _StructureId, "ndb_entity_poly_na_nonstandard");
       } else Table1 = NULL;

       for (i = 0; i < 100; i++) entity_id[i] = -1;
       chain = _molecules[0]->GetFirstChain();
       while (chain) {
            if (chain->chain_type() != ATOMN_TOKEN) {
                 chain = _molecules[0]->GetNextChain();
                 continue;
            }
            for (j = 0; j < n_entity_id; j++) {
                 if (entity_id[j] == chain->entity_id()) break;
            }
            if (j < n_entity_id) {
                 chain = _molecules[0]->GetNextChain();
                 continue;
            }

            entity_id[j] = chain->entity_id();
            n_entity_id++;
            if (Table) {
                 Table->AddRow();
                 sprintf(buff, "%d", entity_id[j]);
                 string.Copy(buff);
                 Table->UpdateCell(string, 0, j);
                 if (chain->na_type() == ATOMN_TYPE_DNA_ONLY)
                      string.Copy("DNA");
                 else if (chain->na_type() == ATOMN_TYPE_RNA_ONLY ||
                          chain->na_type() == ATOMN_TYPE_DNA_RNA)
                      string.Copy("RNA");
                 else if (chain->na_type() == ATOMN_TYPE_TRNA)
                      string.Copy("t-RNA");
                 else string.Copy("RNA");
                 Table->UpdateCell(string, 1, j);
            }

            if (Table1) {
                 e_idx = Table1->GetColumnIndex("entity_id");
                 f_idx = Table1->GetColumnIndex("feature");
                 for (j = 0; j < _n_modifb; j++) {
                      ch1 = _molecules[0]->GetIndexChain(_modifb[j]->FstIndex);
                      ch2 = _molecules[0]->GetIndexChain(_modifb[j]->SndIndex);
                      if (ch1 == chain || ch2 == chain) {
                           sprintf(buff, "%d", chain->entity_id());
                           for (i = 0; i < irow; i++) {
                                Table1->GetCell(string, e_idx, i);
                                Table1->GetCell(string1, f_idx, i);
                                if (string == buff && string1 == "base modification") break;
                           }
                           if (i < irow) continue;
                           Table1->AddRow();
                           string.Copy(buff);
                           Table1->UpdateCell(string, e_idx, irow);
                           string.Copy("base modification");
                           Table1->UpdateCell(string, f_idx, irow);
                           irow++;
                      }
                 }

                 for (j = 0; j < _n_modifp; j++) {
                      ch1 = _molecules[0]->GetIndexChain(_modifp[j]->FstIndex);
                      ch2 = _molecules[0]->GetIndexChain(_modifp[j]->SndIndex);
                      if (ch1 == chain || ch2 == chain) {
                           sprintf(buff, "%d", chain->entity_id());
                           for (i = 0; i < irow; i++) {
                                Table1->GetCell(string, e_idx, i);
                                Table1->GetCell(string1, f_idx, i);
                                if (string == buff && string1 == "phosphate modification") break;
                           }
                           if (i < irow) continue;
                           Table1->AddRow();
                           string.Copy(buff);
                           Table1->UpdateCell(string, e_idx, irow);
                           string.Copy("phosphate modification");
                           Table1->UpdateCell(string, f_idx, irow);
                           irow++;
                      }
                 }

                 for (j = 0; j < _n_modifs; j++) {
                      ch1 = _molecules[0]->GetIndexChain(_modifs[j]->FstIndex);
                      ch2 = _molecules[0]->GetIndexChain(_modifs[j]->SndIndex);
                      if (ch1 == chain || ch2 == chain) {
                           sprintf(buff, "%d", chain->entity_id());
                           for (i = 0; i < irow; i++) {
                                Table1->GetCell(string, e_idx, i);
                                Table1->GetCell(string1, f_idx, i);
                                if (string == buff && string1 == "sugar modification") break;
                           }
                           if (i < irow) continue;
                           Table1->AddRow();
                           string.Copy(buff);
                           Table1->UpdateCell(string, e_idx, irow);
                           string.Copy("sugar modification");
                           Table1->UpdateCell(string, f_idx, irow);
                           irow++;
                      }
                 }

                 residue = chain->GetFirstResidue();
                 while (residue) {
                      drug = find_drug(residue->ResName());
                      if (drug && drug->numModification()) {
                           for (j = 0; j < drug->numModification(); j++) {
                                string2.Copy(drug->Modification(j));
                                string2.ToLower();
                                sprintf(buff, "%d", chain->entity_id());
                                for (i = 0; i < irow; i++) {
                                     Table1->GetCell(string, e_idx, i);
                                     Table1->GetCell(string1, f_idx, i);
                                     if (string == buff && string1 == string2) break;
                                }
                                if (i < irow) continue;
                                Table1->AddRow();
                                string.Copy(buff);
                                Table1->UpdateCell(string, e_idx, irow);
                                Table1->UpdateCell(string2, f_idx, irow);
                                irow++;
                           }
                      }
                      residue = chain->GetNextResidue();
                 }
            }
            chain = _molecules[0]->GetNextChain();
       }
}

void _MAXIT::delete_terminal_atoms()
{
       static int num_aa = 5 /* , num_na = 3 */ ;
       static const char *aa[] = { "OXT", "HXT", "1H", "2H", "3H" };
       // static char *na[] = { "O3P", "3HOP", "H3T" };
       int l = 0, j = 0, k = 0;

       _CHAIN *chain = NULL;
       _RESIDUE *residue = NULL;
       for (l = 0; l < _num_mol; l++) {
            chain = _molecules[l]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() != ATOMP_TOKEN /* &&
                     chain->chain_type() != ATOMN_TOKEN*/ ) {
                      chain = _molecules[l]->GetNextChain();
                      continue;
                 }
                 for (j = 1; j < chain->SeqLen() - 1; j++) {
                      if (chain->SeqRes(j)->Deleted) continue;
                      residue = chain->SeqRes(j)->Residue;
                      if (!residue) continue;
                      // if (chain->SeqRes(j+1)->Residue) continue;
/*
                      if (chain->chain_type() == ATOMP_TOKEN) {
*/
                           for (k = 0; k < num_aa; k++) {
                                residue->delete_a_atom(aa[k]);
                           }
/*
                      } else if (chain->chain_type() == ATOMN_TOKEN) {
                           for (k = 0; k < num_na; k++) {
                                residue->delete_a_atom(na[k]);
                           }
                      }
*/
                 }
                 chain = _molecules[l]->GetNextChain();
            }
       }
}

void _MAXIT::correct_atom_name()
{
       int l, first_residue, need_hydrogen = 0;
       char *p = NULL;
       _CHAIN *chain = NULL;
       _RESIDUE *residue = NULL;

       if (_experiment_type & EXPERIMENT_TYPE_NMR) need_hydrogen = 1;
       for (l = 0; l < _num_mol; l++) {
            chain = _molecules[l]->GetFirstChain();
            while (chain) {
                 first_residue = 0;
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      residue->find_missing_or_extra_atoms(chain->chain_type(),
                              !first_residue, need_hydrogen);
                      p = residue->extras().Text();
                      if (p && strcmp(p, ""))
                           residue->correction_atom_name(_error_message_s);
                      first_residue = 1;
                      residue = chain->GetNextResidue();
                 }
                 chain = _molecules[l]->GetNextChain();
            }
       }
}

void _MAXIT::renumber_pdb_numbers()
{
       int i = 0, found_redundancy = 0, serial_no = 0, start_number = 0;
       char buffer[10];
       _CHAIN *chain = NULL;
       CifString string;
       TblIndexObj index;

       if (!_num_mol || !_molecules) return;

       index.Clear();
       chain = _molecules[0]->GetFirstChain();
       while (chain) {
            if (!strcmp(chain->PDB_ChainID(), "")) {
                 for (i = 0; i < chain->SeqLen(); i++) {
                      if (chain->SeqRes(i)->Deleted) continue;
                      if (!chain->SeqRes(i)->Residue) continue;
                      sprintf(buffer, "%s%s", chain->SeqRes(i)->Field[4],
                                       chain->SeqRes(i)->InsCode);
                      string.Copy(buffer);
                      if (index.Seek(string) >= 0) found_redundancy = 1;
                      index.Add(string);
                      if ((chain->chain_type() == ATOMP_TOKEN ||
                           chain->chain_type() == ATOMN_TOKEN) &&
                          atoi(chain->SeqRes(i)->Field[4]) > serial_no) {
                           serial_no = atoi(chain->SeqRes(i)->Field[4]);
                      }
                 }
            }
            chain = _molecules[0]->GetNextChain();
       }
       if (!found_redundancy) return;

       for (i = 0; i < _num_mol; i++) {
            start_number = serial_no;
            chain = _molecules[i]->GetFirstChain();
            while (chain) {
                 if (!strcmp(chain->PDB_ChainID(), "") &&
                     chain->chain_type() != ATOMP_TOKEN &&
                     chain->chain_type() != ATOMN_TOKEN)
                      chain->update_residues_pdb_numbers(start_number);
                 chain = _molecules[i]->GetNextChain();
            }
       }
}

void _MAXIT::search_complexity()
{
       ISTable *Table = _CifObj->GetTablePtr(_StructureId, "entity");
       if (is_empty_table(Table)) return;

       int rowNo = Table->GetNumRows();
       if (rowNo < 3) return;
       int type_index = Table->GetColumnIndex("type");
       int weight_index = Table->GetColumnIndex("formula_weight");
       if (type_index < 0 || weight_index < 0) return;
       CifString type, weight;
       int count = 0;
       for (int i = 0; i < rowNo; i++) {
            get_value(type, Table, type_index, i);
            get_value(weight, Table, weight_index, i);
            if (!strcasecmp(type.Text(), "POLYMER") && atof(weight.Text()) > 8000.0)
                 count++;
       }
       if (count > 2) printf("-%s-\n", _StructureId);
}

void _MAXIT::generate_symmetry_and_ncs_related_strands(const char *outfilename, const int pdbid_flag)
{
       int i = 0, j = 0, k = 0, add_mol, ret, op = 0, trans[3], len, num_mol_org;
       _CHAIN *chain = NULL, *new_chain = NULL;
       CifString string;
       TblIndexObj index;

       if (_num_mol == 0 || _molecules == NULL) return;

       for (j = 0; j < _num_mol; j++) {
            chain = _molecules[j]->GetFirstChain();
            while (chain) {
                 for (i = 0; i < _n_stbiol; i++) {
                      if (!_stbiol[i]) continue;
                      if (_stbiol[i]->Asym_ID == chain->ChainID() &&
                          !strcmp(_stbiol[i]->Symmetry, NDB_BASE_SYMMETRY)) {
                           chain->set_biol_id(_stbiol[i]->Biol_ID);
                           break;
                      }
                 }
                 chain = _molecules[j]->GetNextChain();
            }
       }

       index.Clear();
       for (i = 0; i < _n_stbiol; i++) {
            if (!_stbiol[i]) continue;
            if (strcmp(_stbiol[i]->Symmetry, NDB_BASE_SYMMETRY)) {
                 string.Copy(_stbiol[i]->Symmetry);
                 ret = index.Seek(string);
                 if (ret < 0) index.Add(string);
            }
       }
       if (index.Root() < 0) {
            generate_ncs_related_strands(outfilename, pdbid_flag);
            return;
       }

       for (i = 0; i < _num_mol; i++) {
            _molecules[i]->convert_between_orthogonal_and_fractional(_cell, NDB_TRANS_ORTHO_TO_FRAC);
       }
       num_mol_org = _num_mol;
       for (k = 0; k < num_mol_org; k++) {
            ret = index.GoFirst();
            while (ret >= 0) {
                 string = index.GetCurrData();
                 op = atoi(string.Text()) - 1;
                 trans[0] = trans[1] = trans[2] = 0;
                 len = strlen(string.Text());
                 for (i = 0; i < len; i++)
                      if (string.Text()[i] == '_') break;
                 if (i+3 < len) {
                      for (j = 0; j < 3; j++)
                           trans[j] = string.Text()[i+j+1] - '5';
                 }
     
                 add_mol = 0;
                 for (i = 0; i < _n_stbiol; i++) {
                      if (!_stbiol[i]) continue;
                      if (string == _stbiol[i]->Symmetry) {
                           chain = _molecules[k]->GetFirstChain();
                           while (chain) {
                                if (_stbiol[i]->Asym_ID == chain->ChainID() &&
                                     chain->op() == 1) break;
                                chain = _molecules[k]->GetNextChain();
                           }
                           if (!chain) continue;
                           new_chain = new _CHAIN;
                           new_chain->symmetry_related_chain(*chain, trans[0], trans[1],
                                       trans[2], &(_cell->symops[op]), 0);
                           new_chain->set_biol_id(_stbiol[i]->Biol_ID);
                           if (!add_mol) {
                                _ManageSpace();
                                _molecules[_num_mol-1]->set_Mol_ID(_num_mol);
                                add_mol = 1;
                           }
                           _molecules[_num_mol-1]->insert_a_chain(new_chain);
                      }
                 }
                 ret = index.GoToNext();
            }
       }
       for (i = 0; i < _num_mol; i++)
            _molecules[i]->convert_between_orthogonal_and_fractional(_cell, NDB_TRANS_FRAC_TO_ORTHO);

}

void _MAXIT::generate_ncs_related_strands(const char *outfilename, const int pdbid_flag)
{
       int i, rowNo, code, m11, m12, m13, m21, m22, m23, m31, m32, m33, v1, v2, v3;
       int numRemark = 0, displayOrder = 0, card_id = 0, irow = 0, Mol_ID = 1;
       int token_id, CONECT_TOKEN;
       double matrix[3][4];
       char buffer[100], pdbid[10];
       _CHAIN *chain = NULL, *new_chain = NULL;
       CifString string;
       FILE *fp = NULL;
       ISTable *Table = NULL;
       _MOLECULE *mol = NULL;
       One_Card_Info_Format *pCard = NULL;

       if (_num_mol == 0 || _molecules == NULL) return;

       Table = _CifObj->GetTablePtr(_StructureId, "struct_ncs_oper");
       if (is_empty_table(Table)) return;

       rowNo = Table->GetNumRows();
       code = Table->GetColumnIndex("code");
       for (i = 0; i < rowNo; i++) {
            Table->GetCell(string, code, i);
            if (string == "generate") break;
       }
       if (i == rowNo) return;

       _create_final_file = 1;

       for (i = 0; i < NUM_REM_TOKEN; i++) {
            token_id = find_card_id(remove_token[i]);
            if (token_id < 0) continue;
            pCard = _CardInfo[token_id].Head();
            while (pCard) pCard = _CardInfo[token_id].delete_a_card(pCard);
       }
       pCard = _CardInfo[HEADER_TOKEN-1].Head();
       if (pCard) pCard->AddElementToField("XXXX", 3);

       strcpy(pdbid, "");
       if (pdbid_flag) {
            pCard = _CardInfo[PDBFIL_TOKEN-1].Head();
            if (pCard) strcpy(pdbid, pCard->Field(1));
            ndb_set_string_to_lower(pdbid);
       }
       if (strcmp(pdbid, ""))
            sprintf(buffer, "%s.pdb1", pdbid);
       else sprintf(buffer, "%s_1", outfilename);

       fp = fopen(buffer, "w");

       _molecules[0]->reorder_chain_order();
       _output_format = NDB_FILE_FORMAT_PDB;
       get_seqres_from_mapping(_output_format);

       for (displayOrder = 0; displayOrder < MxTokens; displayOrder++) {
            for (card_id = 0; card_id < MxTokens; card_id++)
                 if (displayOrder == NdbToken[card_id].DisplayOrder) break;

            if (card_id == MxTokens || card_id + 1 != JRNL_TOKEN &&
                _CardInfo[card_id].Head() == NULL || card_id == NDBSQN_TOKEN-1 ||
                card_id == SYMTRA_TOKEN-1 || (NdbToken[card_id].NdbOnly ||
                card_id + 1 == REMARK_TOKEN || card_id == MASTER_TOKEN-1))
                 continue;

            if (card_id +1 != JRNL_TOKEN)
                 print_general_cards(fp, card_id, NDB_FILE_FORMAT_PDB, numRemark, 0);
            else {
                 print_jrnl_cards(fp, NDB_FILE_FORMAT_PDB, numRemark);
                 print_general_cards(fp, REMARK_TOKEN - 1, NDB_FILE_FORMAT_PDB, numRemark, 0);
            }
       }

       Mol_ID = 1;
       fprintf(fp, "MODEL     %4d                          ", Mol_ID);
       fprintf(fp, "                                        \n");
       print_molecule(_molecules[0], NULL, irow, fp, NDB_FILE_FORMAT_PDB, 0, 1, 0);
       fprintf(fp, "ENDMDL                                  ");
       fprintf(fp, "                                        \n");
       Mol_ID++;

       get_connect_cards();

       m11 = Table->GetColumnIndex("matrix[1][1]");
       m12 = Table->GetColumnIndex("matrix[1][2]");
       m13 = Table->GetColumnIndex("matrix[1][3]");
       m21 = Table->GetColumnIndex("matrix[2][1]");
       m22 = Table->GetColumnIndex("matrix[2][2]");
       m23 = Table->GetColumnIndex("matrix[2][3]");
       m31 = Table->GetColumnIndex("matrix[3][1]");
       m32 = Table->GetColumnIndex("matrix[3][2]");
       m33 = Table->GetColumnIndex("matrix[3][3]");
       v1 = Table->GetColumnIndex("vector[1]");
       v2 = Table->GetColumnIndex("vector[2]");
       v3 = Table->GetColumnIndex("vector[3]");

       for (i = 0; i < rowNo; i++) {
            Table->GetCell(string, code, i);
            if (string != "generate") continue;

            Table->GetCell(string, m11, i);
            matrix[0][0] = atof(string.Text());
            Table->GetCell(string, m12, i);
            matrix[0][1] = atof(string.Text());
            Table->GetCell(string, m13, i);
            matrix[0][2] = atof(string.Text());
            Table->GetCell(string, v1, i);
            matrix[0][3] = atof(string.Text());
            Table->GetCell(string, m21, i);
            matrix[1][0] = atof(string.Text());
            Table->GetCell(string, m22, i);
            matrix[1][1] = atof(string.Text());
            Table->GetCell(string, m23, i);
            matrix[1][2] = atof(string.Text());
            Table->GetCell(string, v2, i);
            matrix[1][3] = atof(string.Text());
            Table->GetCell(string, m31, i);
            matrix[2][0] = atof(string.Text());
            Table->GetCell(string, m32, i);
            matrix[2][1] = atof(string.Text());
            Table->GetCell(string, m33, i);
            matrix[2][2] = atof(string.Text());
            Table->GetCell(string, v3, i);
            matrix[2][3] = atof(string.Text());

            printf("i=%2d\n", i + 1);
            for (int j = 0; j < 3; j++) {
                 for (int k = 0; k < 3; k++) printf("%7.4f ", matrix[j][k]);
                 printf("\n");
            }
            printf("\n");

            mol = new _MOLECULE;
            chain = _molecules[0]->GetFirstChain();
            while (chain) {
                 new_chain = new _CHAIN;
                 new_chain->set_biol_id(1);
                 new_chain->symmetry_related_chain(*chain, matrix);
                 mol->insert_a_chain(new_chain);
                 chain = _molecules[0]->GetNextChain();
            }
            fprintf(fp, "MODEL     %4d                          ", Mol_ID);
            fprintf(fp, "                                        \n");
            print_molecule(mol, NULL, irow, fp, NDB_FILE_FORMAT_PDB, 0, 0, 0);
            fprintf(fp, "ENDMDL                                  ");
            fprintf(fp, "                                        \n");
            Mol_ID++;

            delete mol;
       }

       CONECT_TOKEN = find_card_id("CONECT");
       print_general_cards(fp, CONECT_TOKEN, NDB_FILE_FORMAT_PDB, numRemark, 0);
       print_master_card(fp, numRemark);
       fprintf(fp, "END");
       for (i = 0; i < 77; i++) fprintf(fp, " ");
       fprintf(fp, "\n");
       fclose (fp);
}

void _MAXIT::generate_packing()
{
       if (_num_mol == 0 || _molecules == NULL) return;
       if (strcmp(_cell->space_group, "P 1")) _molecules[0]->crystal_packing(_cell);
}

void _MAXIT::write_pdb_files(const char *outfilename, const int pdbid_flag)
{
       int i, j, *biol_id = NULL, n_biol_id = 0, found = 0, token_id;
       char buffer[100], pdbid[10];
       _CHAIN *chain = NULL;
       One_Card_Info_Format *pCard = NULL;

       if (_create_final_file) return;

       for (i = 0; i < NUM_REM_TOKEN; i++) {
            token_id = find_card_id(remove_token[i]);
            if (token_id < 0) continue;
            pCard = _CardInfo[token_id].Head();
            while (pCard) pCard = _CardInfo[token_id].delete_a_card(pCard);
       }
       pCard = _CardInfo[HEADER_TOKEN-1].Head();
       if (pCard) pCard->AddElementToField("XXXX", 3);

       strcpy(pdbid, "");
       if (pdbid_flag) {
            pCard = _CardInfo[PDBFIL_TOKEN-1].Head();
            if (pCard) strcpy(pdbid, pCard->Field(1));
            ndb_set_string_to_lower(pdbid);
       }

       biol_id = new int[_n_stbiol];
       for (i = 0; i < _n_stbiol; i++) {
            if (!_stbiol[i]) continue;
            for (j = 0; j < n_biol_id; j++) {
                 if (biol_id[j] == _stbiol[i]->Biol_ID) break;
            }
            if (j == n_biol_id) {
                 n_biol_id++;
                 biol_id[j] = _stbiol[i]->Biol_ID;
            }
       }

       for (i = 0; i < n_biol_id; i++) {
            for (j = 0; j < _num_mol; j++) {
                 found = 0;
                 chain = _molecules[j]->GetFirstChain();
                 while (chain) {
                      if (chain->biol_id() == biol_id[i]) {
                           chain->set_display(1);
                           found = 1;
                      } else chain->set_display(0);
                      chain = _molecules[j]->GetNextChain();
                 }
                 if (found) _molecules[j]->set_display(1);
                 else       _molecules[j]->set_display(0);
            }
            if (strcmp(pdbid, ""))
                 sprintf(buffer, "%s.pdb%d", pdbid, i + 1);
            else sprintf(buffer, "%s_%d", outfilename, i + 1);
            set_output_filename(buffer);
            write_ndb_file(NDB_FILE_FORMAT_PDB, 0, 1, 0, 0);
       }
       delete [] biol_id;
}

static _ENTITY *realloc_entity(_ENTITY *entity, const int n_size)
{
       int i;
       _ENTITY *new_entity = new _ENTITY[n_size+1];
       for (i = 0; i <= n_size; i++) {
            new_entity[i].Entity_ID = 0;
            new_entity[i].Old_Entity_ID = 0;
            new_entity[i].Number = 0;
            new_entity[i].Type = NULL;
            new_entity[i].Source = NULL;
            new_entity[i].Description = NULL;
       }
       if (entity) {
            for (i = 0; i < n_size; i++) {
                 new_entity[i].Entity_ID = entity[i].Entity_ID;
                 new_entity[i].Old_Entity_ID = entity[i].Old_Entity_ID;
                 new_entity[i].Number = entity[i].Number;
                 new_entity[i].Type = entity[i].Type;
                 new_entity[i].Source = entity[i].Source;
                 new_entity[i].Description = entity[i].Description;
                 entity[i].Type = NULL;
                 entity[i].Source = NULL;
                 entity[i].Description = NULL;
            }
            delete [] entity;
       }
       return new_entity;
}

static char *get_entity_description(_CHAIN *chain)
{
       int len;
       char *buff = NULL, descriptor[800];
       if (chain->chain_type() == HETAS_TOKEN &&
          (!strcmp(chain->SeqRes(0)->Field[0], "HOH") ||
           !strcmp(chain->SeqRes(0)->Field[0], "DOD"))) {
            len = strlen(NDB_WATER_TEXT)+1;
            buff = new char[len];
            strcpy(buff, NDB_WATER_TEXT);
       } else {
            descriptor[0] = '\0';
            chain->get_descriptor(descriptor);

            if (chain->chain_type() == ATOMS_TOKEN) {
                 len = strlen(descriptor) + 10;
                 buff = new char[len];
                 sprintf(buff, "SUGAR (%s)", descriptor);
            } else if (chain->chain_type() == ATOMN_TOKEN) {
                 if (chain->na_type() == ATOMN_TYPE_RNA_ONLY) {
                      len = strlen(descriptor) + 8;
                      buff = new char[len];
                      sprintf(buff, "%s (%s)", NDB_RNA_TEXT, descriptor);
                 } else if (chain->na_type() == ATOMN_TYPE_TRNA) {
                      len = strlen(descriptor) + 10;
                      buff = new char[len];
                      sprintf(buff, "%s (%s)", NDB_T_RNA_TEXT, descriptor);
                 } else if (chain->na_type() == ATOMN_TYPE_DNA_RNA) {
                      len = strlen(descriptor) + 12;
                      buff = new char[len];
                      sprintf(buff, "%s (%s)", NDB_HYBRID_TEXT, descriptor);
                 } else {
                      len = strlen(descriptor) + 8;
                      buff = new char[len];
                      sprintf(buff, "%s (%s)", NDB_DNA_TEXT, descriptor);
                 }
            } else {
                 len = strlen(descriptor);
                 if (len) {
                      buff = new char[len+1];
                      strcpy(buff, descriptor);
                 }
            }
       }
       return buff;
}

static char *get_line_from_string(char *line, char *string)
{
       unsigned int i;

       if (!string || *string == '\0') return NULL;
       *line = '\0';

       for (i = 0; i < strlen(string); i++) {
            if (string[i] == '\n') break;
            *line = string[i];
            line++;
       }
       *line = '\0';
       i++;
       if (string[i] == '\0') return NULL;
       return &(string[i]);
}
