/*
FILE:     cif_standard.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 <sys/stat.h>

#include "DICFileObj.h"
#include "ndb_misclib.h"
#include "_maxit.h"
#include "bond_angle_torsion.h"
#include "erf_category.h"
#include "erf_util.h"
#include "util.h"
#include "cif_standard.h"

#define  NS    10
#define  dictxraybinfile  "mmcif_xray.odb"
#define  dictnmrbinfile   "mmcif_nmr.odb"

static void remove_infinite(ISTable *Table, const int id_idx);
static void remove_comma(ISTable *Table, const int id_idx);
static void remove_k(ISTable *Table, const int col, const int row);
static void update_index(ISTable *Table, const int idx, ISTable *Table1, const int idx1);
static ISTable *get_extend_table(ISTable *Table, _PDB_INSERT_CODE *Miss_Man_I_cat);
static int find_index(SCHEME_TABLE *scheme, const char *chnid, CifString &num);
static void AddMandatoryItem(CifFileObj *in, const int experiment_type);
static void GetKeywordFromItemName(char *keyword, const char *itemName);
static void ConvertEscapedString(CifString& inString, CifString& outString);

void _MAXIT::ndbcif_postprocessing_cif_standard(const int standardized,
               const int keep_contact_author)
{
       ISTable *Table = NULL, *Table1 = NULL, *Table2 = NULL, *Table3 = NULL;
       CifString string, id, old_id, text, beg, end;
       char buffer[1000], chnid[10], *p, *q, buff[1000], buff1[1000];
       char **categoryName = NULL, classification[4][20];
       int i, j, k, l, m, id_idx, irow, rowNo, rowNo1, colNo, mon_id;
       int type_idx = -1, mon_idx = -1, name_idx = -1, syno_idx = -1, form_idx = -1, wght_idx = -1;
       int auth_seq_beg, auth_seq_end, seq_beg, seq_end, chn_idx = -1;
       int auth_beg_ins, auth_end_ins, idx[NUM_ITEM];
       int found_gap, last, seq_num, align_id, align_id1, numCategory = 0;
       int ndb_chain_id, rowNo2, entity_id, pdb_id, index1, index2, index3, index4;
       int mol_id, num1, num2, num3, num4, len;
       ReVarCifArray<CifString> *ColumnNames = NULL;
       double weight;
       ConnectFormat *drug = NULL;
       SCHEME_TABLE *scheme = NULL;
       _ATOM *atom = NULL, **at1 = NULL, **at2 = NULL, **at3 = NULL, **at4 = NULL;
       _RESIDUE *residue = NULL, *prev = NULL, *curr = NULL;
       _CHAIN *chain = NULL;

       Table = _CifObj->GetTablePtr(_StructureId, "computing");
       Table1 = _CifObj->GetTablePtr(_StructureId, "software");
       if (!is_empty_table(Table) && is_empty_table(Table1)) {
            _CifObj->DeleteTable(_StructureId, "software");
            Table1 = new ISTable("software");
            Table1->AddColumn("name");
            Table1->AddColumn("classification");
            Table1->AddColumn("version");
            Table1->AddColumn("citation_id");
            idx[0] = Table->GetColumnIndex("data_collection");
            strcpy(classification[0], "data collection");
            idx[1] = Table->GetColumnIndex("data_reduction");
            strcpy(classification[1], "data reduction");
            idx[2] = Table->GetColumnIndex("structure_solution");
            strcpy(classification[2], "model building");
            idx[3] = Table->GetColumnIndex("structure_refinement");
            strcpy(classification[3], "refinement");
            irow = 0;
            for (i = 0; i < 4; i++) {
                 if (idx[i] < 0) continue;
                 Table->GetCell(string, idx[i], 0);
                 if (string == "" || string == "." || string == "?") continue;
                 strcpy(buff, string.Text());
                 len = strlen(buff);
                 for (j = 0; j < len; j++) {
                      if (buff[j] == ';') buff[j] = ',';
                 }
                 p = strstr(buff, " AND ");
                 if (p) {
                      *(p + 1) = ' ';
                      *(p + 2) = ',';
                      *(p + 3) = ' ';
                 }
                 p = strstr(buff, " and ");
                 if (p) {
                      *(p + 1) = ' ';
                      *(p + 2) = ',';
                      *(p + 3) = ' ';
                 }
                 p = strstr(buff, " And ");
                 if (p) {
                      *(p + 1) = ' ';
                      *(p + 2) = ',';
                      *(p + 3) = ' ';
                 }
                 q = buff;
                 while (q) {
                      q = get_words_separate_by_delimit(q, buff1, ',');
                      Table1->AddRow();
                      p = get_word(buff1, buffer);
                      text.Copy(buffer);
                      Table1->UpdateCell(text, 0, irow);
                      text.Copy(classification[i]);
                      Table1->UpdateCell(text, 1, irow);
                      if (p) text.Copy(p);
                      else   text.Copy(".");
                      Table1->UpdateCell(text, 2, irow);
                      text.Copy(".");
                      Table1->UpdateCell(text, 3, irow);
                      irow++;
                 }
            }
            _CifObj->WriteTable(Table1, _StructureId, "software");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "ndb_struct_na_base_pair");
       if (!is_empty_table(Table)) {
            rowNo = Table->GetNumRows();
            type_idx = Table->GetColumnIndex("hbond_type_28");
            name_idx = Table->GetColumnIndex("hbond_type_12");
            for (i = 0; i < rowNo; i++) {
                 if (type_idx >= 0) {
                      Table->GetCell(string, type_idx, i);
                      for (j = 0; j < NUM_SAENGER; j++) {
                           if (!strcasecmp(string.Text(), _saenger[j].name)) {
                                sprintf(buffer, "%d", _saenger[j].index);
                                string.Copy(buffer);
                                Table->UpdateCell(string, type_idx, i);
                                break;
                           }
                      }
                 }
                 if (name_idx >= 0) {
                      Table->GetCell(string, name_idx, i);
                      for (j = 0; j < NUM_WESTHOF; j++) {
                           if (!strcasecmp(string.Text(), _westhof[j].name)) {
                                sprintf(buffer, "%d", _westhof[j].index);
                                string.Copy(buffer);
                                Table->UpdateCell(string, name_idx, i);
                                break;
                           }
                      }
                 }
            }
            _CifObj->WriteTable(Table,  _StructureId, "ndb_struct_na_base_pair");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "cell");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("Z_PDB");
            get_value(string, Table, id_idx, 0);
            if (string == "" || string == "0") {
                 string.Copy("1");
                 Table->UpdateCell(string, id_idx, 0);
                 _CifObj->WriteTable(Table, _StructureId, "cell");
            }
       }

       for (k = 0; k < NUM_DIFFRN; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, diffrn_related_categories[k]);
            if (is_empty_table(Table)) continue;

            type_idx = Table->GetColumnIndex("diffrn_id");
            if (type_idx < 0) continue;

            Table1 = _CifObj->GetTablePtr(_StructureId, "diffrn");
            if (!Table1) {
                 Table1 = new ISTable("diffrn");
                 Table1->AddColumn("id");
                 Table1->AddColumn("crystal_id");
            }
            id_idx = Table1->GetColumnIndex("id");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "") {
                      sprintf(buffer, "%d", i + 1);
                      string.Copy(buffer);
                      Table->UpdateCell(string, type_idx, i);
                 }
            }
            update_index(Table, type_idx, Table1, id_idx);
            _CifObj->WriteTable(Table, _StructureId, diffrn_related_categories[k]);
            _CifObj->WriteTable(Table1, _StructureId, "diffrn");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "diffrn_radiation_wavelength");
       if (is_empty_table(Table, "id"))
            _CifObj->DeleteTable(_StructureId, "diffrn_radiation_wavelength");

       Table = _CifObj->GetTablePtr(_StructureId, "diffrn_source");
       if (!is_empty_table(Table)) {
            Table1 = _CifObj->GetTablePtr(_StructureId, "diffrn_radiation_wavelength");
            if (is_empty_table(Table1, "id")) {
                 _CifObj->DeleteTable(_StructureId, "diffrn_radiation_wavelength");
                 Table1 = new ISTable("diffrn_radiation_wavelength");
                 Table1->AddColumn("id");
                 Table1->AddColumn("wavelength");
                 Table1->AddColumn("wt");
            }
            id_idx = Table1->GetColumnIndex("id");
            name_idx = Table1->GetColumnIndex("wavelength");
            irow = Table1->GetNumRows();

            mon_idx = Table->GetColumnIndex("rcsb_wavelength");
            type_idx = Table->GetColumnIndex("rcsb_wavelength_list");
            index1 = Table->GetColumnIndex("source");
            index2 = Table->GetColumnIndex("type");
            index3 = Table->GetColumnIndex("ndb_synchrotron_site");
            index4 = Table->GetColumnIndex("ndb_synchrotron_beamline");
            rowNo = Table->GetNumRows();
  
            for (i = 0; i < rowNo; i++) {
                 get_value(text, Table, index1, i);
                 get_value(beg, Table, index2, i);
                 get_value(end, Table, index3, i);
                 Table->GetCell(string, index4, i);
                 if (!strcasecmp(text.Text(), "Synchrotron") && beg != "" && end == "" &&
                     (string == "" || string == "?" || string == ".")) {
                      beg.ToUpper();
                      separation_string_base_word(beg.Text(), buffer, buff, "BEAMLINE");
                      string.Copy(buffer);
                      Table->UpdateCell(string, index3, i);
                      string.Copy(buff);
                      Table->UpdateCell(string, index4, i);
                 }
                 get_value(string, Table, mon_idx, i);
                 get_value(beg, Table, type_idx, i);
                 if (string == "" && beg == "") continue;

                 string += " ";
                 string += beg;
                 strcpy(buffer, string.Text());
                 len = strlen(buffer);
                 for (j = 0; j < len; j++) {
                      if (buffer[j] == ',' || buffer[j] == ';' || buffer[j] == '-')
                           buffer[j] = ' ';
                 }
                 ndb_clean_string(buffer);

                 p = buffer;
                 while (p) {
                      p = get_word(p, chnid);
                      len = strlen(chnid);
                      for (j = 0; j < len; j++) {
                           if (chnid[j] != '.' && !isdigit(chnid[j])) break;
                      }
                      if (j < len) continue;

                      irow = Table1->GetNumRows();
                      for (j = 0; j < irow; j++) {
                           get_value(beg, Table1, id_idx, j);
                           get_value(end, Table1, name_idx, j);
                           if (beg == "1" && end == "" || end == chnid) break;
                      }
                      if (j < irow) {
                           string.Copy(chnid);
                           Table1->UpdateCell(string, name_idx, j);
                      } else {
                           Table1->AddRow();
                           sprintf(buff, "%d", irow+1);
                           string.Copy(buff);
                           Table1->UpdateCell(string, id_idx, irow);
                           string.Copy(chnid);
                           Table1->UpdateCell(string, name_idx, irow);
                      }
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "diffrn_source");
            if (is_empty_table(Table1, "id")) 
                 // _CifObj->DeleteTable(_StructureId, "diffrn_radiation_wavelength");
                 delete Table1;
            else _CifObj->WriteTable(Table1, _StructureId, "diffrn_radiation_wavelength");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "diffrn_radiation");
       if (!is_empty_table(Table)) {
            rowNo = Table->GetNumRows();
            id_idx = Table->GetColumnIndex("wavelength_id");
            if (id_idx >= 0) {
                 Table1 = _CifObj->GetTablePtr(_StructureId, "diffrn_radiation_wavelength");
                 if (!Table1) {
                      Table1 = new ISTable("diffrn_radiation_wavelength");
                      Table1->AddColumn("id");
                      Table1->AddColumn("wavelength");
                      Table1->AddColumn("wt");
                 }
                 type_idx = Table1->GetColumnIndex("id");
                 update_index(Table, id_idx, Table1, type_idx);
                 if (is_empty_table(Table1)) 
                      _CifObj->DeleteTable(_StructureId, "diffrn_radiation_wavelength");
                 else _CifObj->WriteTable(Table1, _StructureId, "diffrn_radiation_wavelength");
            }
            id_idx = Table->GetColumnIndex("diffrn_id");
            if (id_idx >= 0 && rowNo == 1) {
                 Table->GetCell(string, id_idx, 0);
                 if (string == "" || string == "?") {
                      string.Copy("1");
                      Table->UpdateCell(string, id_idx, 0);
                 }
            }
            id_idx = Table->GetColumnIndex("ndb_monochromatic_or_laue_m_l");
            if (id_idx >= 0) {
                 for (i = 0; i < rowNo; i++) {
                      Table->GetCell(string, id_idx, i);
                      if (string == "N" || string == "n") {
                           string.Copy("M");
                           Table->UpdateCell(string, id_idx, i);
                      }
                      if (string == "Y" || string == "y") {
                           string.Copy("L");
                           Table->UpdateCell(string, id_idx, i);
                      }
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "diffrn_radiation");
       }

       for (k = 0; k < NUM_MISSING_M_I_C; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, _Miss_Man_I_cat[k].category);
            if (is_empty_table(Table)) continue;
            Table1 = get_extend_table(Table, &_Miss_Man_I_cat[k]);
            if (Table1) {
                 _CifObj->DeleteTable(_StructureId, _Miss_Man_I_cat[k].category);
                 _CifObj->WriteTable(Table1, _StructureId, _Miss_Man_I_cat[k].category);
            }
       }

       for (k = 0; k < NUM_MISSIMG_V_I_C; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, _Miss_Value_I_cat[k].category);
            if (is_empty_table(Table)) continue;
            for (i = 0; i < _Miss_Value_I_cat[k].num_items; i++) {
                 idx[i] = Table->GetColumnIndex(_Miss_Value_I_cat[k].items[i]);
            }
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 for (j = 0; j < _Miss_Value_I_cat[k].num_items; j++) {
                      Table->GetCell(string, idx[j], i);
                      if (string == "?" || string == "") {
                           string.Copy(".");
                           Table->UpdateCell(string, idx[j], i);
                      }
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, _Miss_Value_I_cat[k].category);
       }

       Table = _CifObj->GetTablePtr(_StructureId, "refine");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("ndb_ls_sigma_F");
            if (id_idx >= 0) {
                 Table->GetCell(string, id_idx, 0);
                 p = strstr(string.Text(), "COMPLETENESS FOR RANGE (%) :");
                 if (p) {
                      get_word(string.Text(), buffer);
                      id.Copy(buffer);
                      Table->UpdateCell(id, id_idx, 0);
                      p += 28;
                      q = new char[strlen(p)+1];
                      strcpy(q, p);
                      ndb_clean_string(q);
                      if (strcmp(q, "NULL") && strcmp(q, "NONE") &&
                          strcmp(q, "N/A") && strcmp(q, "")) {
                           type_idx = Table->GetColumnIndex("ls_percent_reflns_obs");
                           Table->GetCell(id, type_idx, 0);
                           if (id == "?" || id == "") {
                                id.Copy(q);
                                Table->UpdateCell(id,  type_idx, 0);
                           }
                      }
                      delete [] q;
                 }
            }
            id_idx = Table->GetColumnIndex("B_iso_mean");
            if (id_idx >= 0) {
                 Table->GetCell(string, id_idx, 0);
                 p = strstr(string.Text(), "OVERALL ANISOTROPIC B VALUE");
                 if (p) {
                      get_word(string.Text(), buffer);
                      id.Copy(buffer);
                      Table->UpdateCell(id, id_idx, 0);
                 }
            }
            id_idx = Table->GetColumnIndex("aniso_B[2][3]");
            if (id_idx >= 0) {
                 get_value(string, Table, id_idx, 0);
                 p = strstr(string.Text(), "ESTIM");
                 if (p) {
                      *p = '\0';
                      ndb_clean_string(string.Text());
                      Table->UpdateCell(string, id_idx, 0);
                 }
            }
            id_idx = Table->GetColumnIndex("ls_d_res_low");
            remove_infinite(Table, id_idx);

            id_idx = Table->GetColumnIndex("ls_number_reflns_obs");
            remove_comma(Table, id_idx);

            id_idx = Table->GetColumnIndex("ls_number_reflns_all");
            remove_comma(Table, id_idx);

            _CifObj->WriteTable(Table, _StructureId, "refine");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "refine_hist");
       Table1 = _CifObj->GetTablePtr(_StructureId, "refine");
       if (!is_empty_table(Table) && !is_empty_table(Table1)) {
            id_idx   = Table1->GetColumnIndex("ls_d_res_high");
            type_idx = Table1->GetColumnIndex("ls_d_res_low");
            get_value(id, Table1, id_idx, 0);
            get_value(text, Table1, type_idx, 0);
            if (id != "") {
                 id_idx   = Table->GetColumnIndex("d_res_high");
                 Table->UpdateCell(id, id_idx, 0);
            }
            if (text != "") {
                 type_idx = Table->GetColumnIndex("d_res_low");
                 Table->UpdateCell(text, type_idx, 0);
            }
            _CifObj->WriteTable(Table, _StructureId, "refine_hist");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "struct_ref");
       Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq");
       Table2 = _CifObj->GetTablePtr(_StructureId, "ndb_poly_seq_scheme");
       if (!is_empty_table(Table) && !is_empty_table(Table1) && !is_empty_table(Table2)) {
            rowNo = Table->GetNumRows();
            id_idx = Table->GetColumnIndex("id");
            type_idx = Table->GetColumnIndex("entity_id");

            rowNo1 = Table1->GetNumRows();
            align_id = Table1->GetColumnIndex("ref_id");
            ndb_chain_id = Table1->GetColumnIndex("ndb_chain_id");

            rowNo2 = Table2->GetNumRows();
            entity_id = Table2->GetColumnIndex("entity_id");
            pdb_id = Table2->GetColumnIndex("pdb_id");
            for (i = 0; i < rowNo; i++) {
                 get_value(text, Table, id_idx, i);
                 found_gap = 0;
                 if (text != "") {
                      for (j = 0; j < rowNo1; j++) {
                           get_value(beg, Table1, align_id, j);
                           if (beg == text) {
                                get_value(old_id, Table1, ndb_chain_id, j);
                                for (k = 0; k < rowNo2; k++) {
                                     get_value(end, Table2, pdb_id, k);
                                     if (old_id == end) {
                                          found_gap = 1;
                                          Table2->GetCell(string, entity_id, k);
                                          Table->UpdateCell(string, type_idx, i);
                                          break;
                                     }
                                }
                           }
                           if (found_gap) break;
                      }
                 }
                 if (!found_gap) {
                      string.Copy(".");
                      Table->UpdateCell(string, type_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "struct_ref");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "reflns");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("d_resolution_low");
            remove_infinite(Table, id_idx);

            id_idx = Table->GetColumnIndex("number_obs");
            remove_comma(Table, id_idx);

            id_idx = Table->GetColumnIndex("number_all");
            remove_comma(Table, id_idx);
            _CifObj->WriteTable(Table, _StructureId, "reflns");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "rcsb_nmr_spectrometer");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("field_strength");
            if (id_idx >= 0) {
                 rowNo = Table->GetNumRows();
                 for (i = 0; i < rowNo; i++) {
                      get_value(string, Table, id_idx, i);
                      if (string != "") {
                           q = new char[strlen(string.Text())+1];
                           strcpy(q, string.Text());
                           ndb_clean_string(q);
                           string.Copy(q);
                           Table->UpdateCell(string, id_idx, i);
                           delete [] q;
                      }
                 }
                 _CifObj->WriteTable(Table, _StructureId, "rcsb_nmr_spectrometer");
            }
       }

       for (k = 0; k < NUM_SYMM_V_C; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, _Symmetry_Value_Cat[k].category);
            if (is_empty_table(Table)) continue;
            for (i = 0; i < _Symmetry_Value_Cat[k].num_items; i++) {
                 idx[i] = Table->GetColumnIndex(_Symmetry_Value_Cat[k].items[i]);
            }
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 for (j = 0; j < _Symmetry_Value_Cat[k].num_items; j++) {
                      get_value(string, Table, idx[j], i);
                      if (string != "") {
                           strcpy(buffer, string.Text());
                           l = strlen(buffer);
                           for (m = 0; m < l; m++)
                                if (buffer[m] == '_') break;
                           if (m == l) {
                                for (m = 0; m < (l - 3); m++)
                                     chnid[m] = buffer[m];
                                chnid[m] = '_';
                                for (m = (l - 3); m < l; m++)
                                     chnid[m+1] = buffer[m];
                                chnid[m+1] = '\0';
                                string.Copy(chnid);
                                Table->UpdateCell(string, idx[j], i);
                           }
                      }
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, _Symmetry_Value_Cat[k].category);
       }

       if (_num_mol) {
            _CifObj->DeleteTable(_StructureId, "chem_comp");
            Table = new ISTable("chem_comp");
            Table->AddColumn("id");
            Table->AddColumn("type");
            Table->AddColumn("mon_nstd_flag");
            Table->AddColumn("name");
            Table->AddColumn("ndb_synonyms");
            Table->AddColumn("formula");
            Table->AddColumn("formula_weight");

            _CifObj->DeleteTable(_StructureId, "atom_type");
            Table1 = new ISTable("atom_type");
            Table1->AddColumn("symbol");
            
            chain = _molecules[_rep_mol]->GetFirstChain();
            while (chain) {
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      atom = residue->GetFirstAtom();
                      while (atom) {
                           rowNo = Table->GetNumRows();
                           rowNo1 = Table1->GetNumRows();
                           for (i = 0; i < rowNo; i++) {
                                get_value(string, Table, 0, i);
                                if (string == atom->restype()) break;
                           }
                           if (i == rowNo) {
                                Table->AddRow();
                                string.Copy(atom->restype());
                                Table->UpdateCell(string, 0, i);
                           }
                           for (i = 0; i < rowNo1; i++) {
                                get_value(string, Table1, 0, i);
                                if (string == atom->atom_type()) break;
                           }
                           if (i == rowNo1) {
                                Table1->AddRow();
                                string.Copy(atom->atom_type());
                                Table1->UpdateCell(string, 0, i);
                           }
                           atom = residue->GetNextAtom();
                      }
                      residue = chain->GetNextResidue();
                 }
                 chain = _molecules[_rep_mol]->GetNextChain();
            }
            _CifObj->WriteTable(Table1, _StructureId, "atom_type");
            
            Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
            if (!is_empty_table(Table1)) {
                 mon_id = Table1->GetColumnIndex("db_mon_id");
                 update_index(Table1, mon_id, Table, 0);
            }

            Table1 = _CifObj->GetTablePtr(_StructureId, "entity_poly_seq");
            if (!is_empty_table(Table1)) {
                 mon_id = Table1->GetColumnIndex("mon_id");
                 update_index(Table1, mon_id, Table, 0);
            }

            rowNo = Table->GetNumRows();
            id_idx = Table->GetColumnIndex("id");
            type_idx = Table->GetColumnIndex("type");
            mon_idx = Table->GetColumnIndex("mon_nstd_flag");
            name_idx = Table->GetColumnIndex("name");
            syno_idx = Table->GetColumnIndex("ndb_synonyms");
            form_idx = Table->GetColumnIndex("formula");
            wght_idx = Table->GetColumnIndex("formula_weight");
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, id_idx, i);
                 drug = find_drug(string.Text());
                 if (!drug) {
                      if (!strcmp(string.Text(), "UNL")) {
                           string.Copy("UNKNOWN LIGAND");
                           Table->UpdateCell(string, name_idx, i);
                           string.Copy("non-polymer");
                           Table->UpdateCell(string, type_idx, i);
                           string.Copy(".");
                           Table->UpdateCell(string, mon_idx, i);
                      }
                      continue;
                 }
                 weight = get_molecule_weight(string.Text());
                 string.Copy(drug->type());
                 string.ToLower();
                 if (string == "d-peptide linking")
                      string.Copy("D-peptide linking");
                 else if (string == "l-peptide linking")
                      string.Copy("L-peptide linking");
                 else if (string == "d-peptide nh3 amino terminus")
                      string.Copy("D-peptide NH3 amino terminus");
                 else if (string == "l-peptide nh3 amino terminus")
                      string.Copy("L-peptide NH3 amino terminus");
                 else if (string == "d-peptide cooh carboxy terminus")
                      string.Copy("D-peptide COOH carboxy terminus");
                 else if (string == "l-peptide cooh carboxy terminus")
                      string.Copy("L-peptide COOH carboxy terminus");
                 else if (string == "dna linking")
                      string.Copy("DNA linking");
                 else if (string == "rna linking")
                      string.Copy("RNA linking");
                 else if (string == "d-saccharide")
                      string.Copy("D-saccharide");
                 else if (string == "l-saccharide")
                      string.Copy("L-saccharide");
                 Table->UpdateCell(string, type_idx, i);

                 if (is_a_standard_residue(drug->drugname()) &&
                     strcmp(drug->drugname(), "N") && strcmp(drug->drugname(), "UNK"))
                      string.Copy("y");
                 else if (drug->parent_residue() && strcmp(drug->parent_residue(), ""))
                      string.Copy("n");
                 else string.Copy(".");
                 Table->UpdateCell(string, mon_idx, i);

                 if (drug->chemical_name()) {
                      string.Copy(drug->chemical_name());
                      Table->UpdateCell(string, name_idx, i);
                 }

                 if (drug->synonym()) {
                      string.Copy(drug->synonym());
                      Table->UpdateCell(string, syno_idx, i);
                 }

                 if (drug->formula()) {
                      if (drug->formal_charge() && strcmp(drug->formal_charge(), "") &&
                          strcmp(drug->formal_charge(), "0")) {
                           sprintf(buffer, "%s %s", drug->formula(), drug->formal_charge());
                           string.Copy(buffer);
                      } else string.Copy(drug->formula());
                      Table->UpdateCell(string, form_idx, i);
                 }

                 if (weight > 0.0) {
                      sprintf(buffer, "%.3f", weight);
                      string.Copy(buffer);
                 } else string.Copy("");
                 Table->UpdateCell(string, wght_idx, i);
            }
            _CifObj->WriteTable(Table, _StructureId, "chem_comp");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "entry");
       if (is_empty_table(Table)) {
            if (!Table) {
                 Table = new ISTable("entry");
                 Table->AddColumn("id");
                 Table->AddRow();
            }
            id_idx = Table->GetColumnIndex("id");
            string.Copy(_StructureId);
            Table->UpdateCell(string, id_idx, 0);
            _CifObj->WriteTable(Table, _StructureId, "entry");
       }

       Table1 = _CifObj->GetTablePtr(_StructureId, "struct_biol_gen");
       if (Table1) {
            type_idx = Table1->GetColumnIndex("biol_id");

            Table = _CifObj->GetTablePtr(_StructureId, "struct_biol");
            if (is_empty_table(Table) && Table) {
                 _CifObj->DeleteTable(_StructureId, "struct_biol");
                 Table = NULL;
            }
            if (!Table) {
                 Table = new ISTable("struct_biol");
                 Table->AddColumn("id");
            }
            id_idx = Table->GetColumnIndex("id");
            update_index(Table1, type_idx, Table, id_idx);
            _CifObj->WriteTable(Table, _StructureId, "struct_biol");
       }

       Table1 = _CifObj->GetTablePtr(_StructureId, "database_PDB_remark");
       if (!is_empty_table(Table1)) {
            rowNo = Table1->GetNumRows();
            id_idx = Table1->GetColumnIndex("id");
            type_idx = Table1->GetColumnIndex("text");
            if (id_idx >= 0 && type_idx >= 0) {
                 old_id.Copy("-1");
                 j = 0;
                 for (i = 0; i < rowNo; i++) {
                      Table1->GetCell(id, id_idx, i);
                      if (id != old_id)
                           old_id.Copy(id);
                      else break;
                 }
                 if (i < rowNo) {
                      Table = new ISTable("database_PDB_remark");
                      Table->AddColumn("id");
                      Table->AddColumn("text");
                      old_id.Copy("-1");
                      text.Copy("");
                      irow = 0;
                      for (i = 0; i < rowNo; i++) {
                           Table1->GetCell(id, id_idx, i);
                           Table1->GetCell(string, type_idx, i);
                           if (string == "?" || string == ".")
                                string.Copy("");
                           if (id != old_id) {
                                if (text != "") {
                                     Table->AddRow();
                                     Table->UpdateCell(old_id, 0, irow);
                                     Table->UpdateCell(text, 1, irow);
                                     irow++;
                                }
                                text.Copy(string);
                                old_id.Copy(id);
                           } else {
                                text += '\n';
                                text += string;
                           }
                      }
                      if (text != "") {
                           Table->AddRow();
                           Table->UpdateCell(old_id, 0, irow);
                           Table->UpdateCell(text, 1, irow);
                      }
                      _CifObj->DeleteTable(_StructureId, "database_PDB_remark");
                      _CifObj->WriteTable(Table, _StructureId, "database_PDB_remark");
                 }
            }
       }

       for (k = 0; k < NUM_CAT_ITEM; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, _cat_items[k].category);
            if (is_empty_table(Table)) continue;
            type_idx = Table->GetColumnIndex(_cat_items[k].item);
            if (type_idx < 0) continue;

            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "") continue;
                 strcpy(buffer, string.Text());
                 ndb_convert_time_format(NDB_FILE_FORMAT_NDB, buffer, buffer, 11);
                 string.Copy(buffer);
                 Table->UpdateCell(string, type_idx, i);
            }
            _CifObj->WriteTable(Table, _StructureId, _cat_items[k].category);
       }

       Table = _CifObj->GetTablePtr(_StructureId, "diffrn");
       if (!is_empty_table(Table)) {
            rowNo = Table->GetNumRows();
            id_idx = Table->GetColumnIndex("ambient_temp");
            type_idx = Table->GetColumnIndex("crystal_id");
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "") {
                      string.Copy("1");
                      Table->UpdateCell(string, type_idx, i);
                 }
                 remove_k(Table, id_idx, i);
            }
            _CifObj->WriteTable(Table, _StructureId, "diffrn");

            Table1 = _CifObj->GetTablePtr(_StructureId, "exptl_crystal");
            if (!Table1) {
                 Table1 = new ISTable("exptl_crystal");
                 Table1->AddColumn("id");
            }
            id_idx = Table1->GetColumnIndex("id");
            update_index(Table, type_idx, Table1, id_idx);
            _CifObj->WriteTable(Table1, _StructureId, "exptl_crystal");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "diffrn_radiation_wavelength");
       if (!is_empty_table(Table)) {
            name_idx = Table->GetColumnIndex("wavelength");
            type_idx = Table->GetColumnIndex("wt");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 Table->GetCell(string, name_idx, i);
                 if (string == "" || string == "?") {
                      string.Copy(".");
                      Table->UpdateCell(string, name_idx, i);
                 }
                 Table->GetCell(string, type_idx, i);
                 if (string == "" || string == "?" || string == ".") {
                      string.Copy("1.0");
                      Table->UpdateCell(string, type_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "diffrn_radiation_wavelength");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "rcsb_nmr_exptl_sample_conditions");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("temperature");
            type_idx = Table->GetColumnIndex("pressure");
            name_idx = Table->GetColumnIndex("pressure_units");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 remove_k(Table, id_idx, i);
                 if (type_idx >= 0) {
                      get_value(string, Table, type_idx, i);
                      get_value(beg, Table, name_idx, i);
                      if (string == "1 ATM") {
                           string.Copy("1");
                           Table->UpdateCell(string, type_idx, i);
                           string.Copy("ATM");
                           Table->UpdateCell(string, name_idx, i);
                      } else {
                           if (beg == "" && isxdigit(string.Text()[0]) &&
                               isxdigit(string.Text()[strlen(string.Text())-1])) {
                                string.Copy("ATM");
                                Table->UpdateCell(string, name_idx, i);
                           }
                      }
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "rcsb_nmr_exptl_sample_conditions");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "exptl_crystal_grow");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("temp");
            type_idx = Table->GetColumnIndex("pH");
            mon_idx = Table->GetColumnIndex("rcsb_pH_range");
            name_idx = Table->GetColumnIndex("crystal_id");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 remove_k(Table, id_idx, i);
                 get_value(string, Table, type_idx, i);
                 if (string != "") {
                      strcpy(buffer, string.Text());
                      len = strlen(buffer);
                      for (j = 0; j < len; j++) {
                           if (!isdigit(buffer[j]) && buffer[j] != '.') break;
                      }
                      if (j < len) {
                           string.Copy("");
                           Table->UpdateCell(string, type_idx, i);
                           string.Copy(buffer);
                           Table->UpdateCell(string, mon_idx, i);
                      }
                 }
                 get_value(string, Table, name_idx, i);
                 if (string == "") {
                      string.Copy("1");
                      Table->UpdateCell(string, name_idx, i);
                 }
                 get_value(string, Table, mon_idx, i);
                 if (string == "") continue;
                 strcpy(buffer, string.Text());
                 len = strlen(buffer);
                 for (j = 0; j < len; j++) {
                      if (!isdigit(buffer[j]) && buffer[j] != '.') break;
                 }
                 if (j == len) {
                      string.Copy("");
                      Table->UpdateCell(string, mon_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "exptl_crystal_grow");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "exptl_crystal_grow_comp");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("crystal_id");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, id_idx, i);
                 if (string == "") {
                      string.Copy("1");
                      Table->UpdateCell(string, name_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "exptl_crystal_grow_comp");
       }

       _CifObj->DeleteTable(_StructureId, "struct_mon_prot_cis");
       Table = new ISTable("struct_mon_prot_cis");
       Table->AddColumn("id");
       Table->AddColumn("label_comp_id");
       Table->AddColumn("label_seq_id");
       Table->AddColumn("label_asym_id");
       Table->AddColumn("label_alt_id");
       Table->AddColumn("ndb_label_ins_code");
       Table->AddColumn("auth_comp_id");
       Table->AddColumn("auth_seq_id");
       Table->AddColumn("auth_asym_id");
       Table->AddColumn("ndb_auth_ins_code");
       Table->AddColumn("ndb_label_comp_id_2");
       Table->AddColumn("ndb_label_seq_id_2");
       Table->AddColumn("ndb_label_asym_id_2");
       Table->AddColumn("ndb_label_ins_code_2");
       Table->AddColumn("ndb_auth_comp_id_2");
       Table->AddColumn("ndb_auth_seq_id_2");
       Table->AddColumn("ndb_auth_asym_id_2");
       Table->AddColumn("ndb_auth_ins_code_2");
       Table->AddColumn("ndb_model_num");
       Table->AddColumn("ndb_omega_angle");

       irow = 0;
       for (i = 0; i < _num_mol; i++) {
            mol_id = _molecules[i]->Mol_ID();
            chain = _molecules[i]->GetFirstChain();
            while (chain) {
                 if (chain->chain_type() != ATOMP_TOKEN &&
                     chain->chain_type() != ATOMN_TOKEN) {
                      chain = _molecules[i]->GetNextChain();
                      continue;
                 }
                 prev = NULL;
                 curr = chain->GetFirstResidue();
                 while (curr) {
                      if (prev && curr) {
                           at1 = prev->find_atom("CA", num1);
                           at2 = prev->find_atom("C",  num2);
                           at3 = curr->find_atom("N",  num3);
                           at4 = curr->find_atom("CA", num4);
                           if (at1 && at2 && at3 && at4 && is_a_bond(at2[0], at3[0])) {
                                weight = cal_torsion(at1[0], at2[0], at3[0], at4[0]);
                                weight = mod(weight, -180, 180);
                                if (fabs(weight) <= 30.0) {
                                     Table->AddRow();
                                     sprintf(buffer, "%d", irow+1);
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 0, irow);
                                     string.Copy(prev->ResName());
                                     Table->UpdateCell(string, 1, irow);
                                     Table->UpdateCell(string, 6, irow);
                                     sprintf(buffer, "%d", prev->GetFirstAtom()->resnum());
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 2, irow);
                                     string.Copy(chain->ChainID());
                                     Table->UpdateCell(string, 3, irow);
                                     Table->UpdateCell(string, 12, irow);
                                     string.Copy(".");
                                     Table->UpdateCell(string, 4, irow);
                                     string.Copy(prev->GetFirstAtom()->ins_code());
                                     Table->UpdateCell(string, 5, irow);
                                     Table->UpdateCell(string, 9, irow);
                                     sprintf(buffer, "%d", prev->GetFirstAtom()->pdb_resnum());
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 7, irow);
                                     string.Copy(chain->PDB_ChainID());
                                     Table->UpdateCell(string, 8, irow);
                                     Table->UpdateCell(string, 16, irow);
                                     string.Copy(curr->ResName());
                                     Table->UpdateCell(string, 10, irow);
                                     Table->UpdateCell(string, 14, irow);
                                     sprintf(buffer, "%d", curr->GetFirstAtom()->resnum());
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 11, irow);
                                     string.Copy(curr->GetFirstAtom()->ins_code());
                                     Table->UpdateCell(string, 13, irow);
                                     Table->UpdateCell(string, 17, irow);
                                     sprintf(buffer, "%d", curr->GetFirstAtom()->pdb_resnum());
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 15, irow);
                                     sprintf(buffer, "%d", mol_id);
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 18, irow);
                                     sprintf(buffer, "%.2f", weight);
                                     string.Copy(buffer);
                                     Table->UpdateCell(string, 19, irow);
                                     irow++;
                                }
                           }
                           if (at1) delete [] at1;
                           if (at2) delete [] at2;
                           if (at3) delete [] at3;
                           if (at4) delete [] at4;
                      }
                      prev = curr;
                      curr = chain->GetNextResidue();
                 }
                 chain = _molecules[i]->GetNextChain();
            }
       }
       _CifObj->WriteTable(Table, _StructureId, "struct_mon_prot_cis");

       for (k = 0; k < NUM_STRUCT_SITE; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, _struct_sites[k].category);
            if (is_empty_table(Table)) continue;
            type_idx = Table->GetColumnIndex(_struct_sites[k].item);
            if (type_idx < 0) continue;
            Table1 = _CifObj->GetTablePtr(_StructureId, "struct_site");
            if (is_empty_table(Table1) && Table1) {
                 _CifObj->DeleteTable(_StructureId, "struct_site");
                 Table1 = NULL;
            }
            if (!Table1) {
                 Table1 = new ISTable("struct_site");
                 Table1->AddColumn("id");
                 Table1->AddColumn("details");
            }
            id_idx = Table1->GetColumnIndex("id");
            update_index(Table, type_idx, Table1, id_idx);
            _CifObj->WriteTable(Table1, _StructureId, "struct_site");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "refine_B_iso");
       if (!is_empty_table(Table)) {
            rowNo = Table->GetNumRows();
            type_idx = Table->GetColumnIndex("treatment");
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "I") {
                      string.Copy("isotropic");
                      Table->UpdateCell(string, type_idx, i);
                 }  else if (string == "A") {
                      string.Copy("anisotropic");
                      Table->UpdateCell(string, type_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "refine_B_iso");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "refine_occupancy");
       if (!is_empty_table(Table)) {
            rowNo = Table->GetNumRows();
            type_idx = Table->GetColumnIndex("treatment");
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "OF") {
                      string.Copy("fix");
                      Table->UpdateCell(string, type_idx, i);
                 } else if (string == "OR") {
                      string.Copy("ref");
                      Table->UpdateCell(string, type_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "refine_occupancy");
       }

       Table3 = _CifObj->GetTablePtr(_StructureId, "ndb_poly_seq_scheme");
       if (is_empty_table(Table3))
            ndb_log_message(NDB_MSG_ERR, "%s: empty ndb_poly_seq_scheme category\n",
                           _StructureId);
       else {
            char **tmp;
            PAIR_INDEX **tmp_index;
            CIF_PDB_PAIR **tmp_pair;
            scheme = new SCHEME_TABLE;
            scheme->num = 0;
            scheme->chnid = NULL;
            scheme->index = NULL;
            rowNo = Table3->GetNumRows();
            chn_idx = Table3->GetColumnIndex("pdb_id");
            seq_beg = Table3->GetColumnIndex("seq_id");
            seq_end = Table3->GetColumnIndex("pdb_num");
            strcpy(buffer, "XXX");
            j = 0;
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table3, chn_idx, i);
                 strcpy(chnid, string.Text());
                 get_value(beg, Table3, seq_beg, i);
                 get_value(end, Table3, seq_end, i);
                 if (strcmp(buffer, chnid)) {
                      strcpy(buffer, chnid);
                      j = scheme->num;
                      scheme->num++;
                      tmp = scheme->chnid;
                      scheme->chnid = new char*[scheme->num];
                      for (k = 0; k < j; k++) scheme->chnid[k] = tmp[k];
                      delete [] tmp;
                      scheme->chnid[j] = new char[5];
                      strcpy(scheme->chnid[j], chnid);
                      tmp_index = scheme->index;
                      scheme->index = new PAIR_INDEX*[scheme->num];
                      for (k = 0; k < j; k++) scheme->index[k] = tmp_index[k];
                      delete [] tmp_index;
                      scheme->index[j] = new PAIR_INDEX;
                      scheme->index[j]->length = 0;
                      scheme->index[j]->pair = NULL;
                 }
                 scheme->index[j]->length++;
                 tmp_pair = scheme->index[j]->pair;
                 scheme->index[j]->pair = new CIF_PDB_PAIR*[scheme->index[j]->length];
                 for (k = 0; k < scheme->index[j]->length - 1; k++)
                      scheme->index[j]->pair[k] = tmp_pair[k];
                 delete [] tmp_pair;
                 scheme->index[j]->pair[scheme->index[j]->length-1] = new CIF_PDB_PAIR;
                 strcpy(scheme->index[j]->pair[scheme->index[j]->length-1]->cif, beg.Text());
                 strcpy(scheme->index[j]->pair[scheme->index[j]->length-1]->pdb, end.Text());
            }
            for (i = 0; i < scheme->num; i++) {
                 found_gap = 0;
                 last = -1;
                 for (j = 0; j < scheme->index[i]->length; j++) {
                      if (!strcmp(scheme->index[i]->pair[j]->pdb, "") ||
                          !strcmp(scheme->index[i]->pair[j]->pdb, "?"))
                           found_gap = 1;
                      else {
                           last = j;
                           if (found_gap) {
                                seq_num = atoi(scheme->index[i]->pair[j]->pdb);
                                for (k = j - 1; k >= 0; k--) {
                                     if (strcmp(scheme->index[i]->pair[k]->pdb, "") &&
                                         strcmp(scheme->index[i]->pair[k]->pdb, "?"))
                                          break;
                                     seq_num--;
                                     sprintf(scheme->index[i]->pair[k]->pdb, "%d", seq_num);
                                }
                                found_gap = 0;
                           }
                      }
                 }
                 if (found_gap && last >= 0) {
                      seq_num = atoi(scheme->index[i]->pair[last]->pdb);
                      for (k = last + 1; k < scheme->index[i]->length; k++) {
                           if (strcmp(scheme->index[i]->pair[k]->pdb, "") &&
                               strcmp(scheme->index[i]->pair[k]->pdb, "?"))
                                break;
                           seq_num++;
                           sprintf(scheme->index[i]->pair[k]->pdb, "%d", seq_num);
                      }
                 }
            }
/*
            if (standardized) {
                 rowNo = Table3->GetNumRows();
                 colNo = Table3->GetNumColumns();
                 for (i = 0; i < rowNo; i++) {
                      for (j = 0; j < colNo; j++) {
                           Table3->GetCell(string, j, i);
                           if (string == "...") {
                                string.Copy("n/a");
                                Table3->UpdateCell(string, j, i);
                           }
                      }
                 }
                 _CifObj->WriteTable(Table3, _StructureId, "ndb_poly_seq_scheme");
            }
*/
       }

       Table = _CifObj->GetTablePtr(_StructureId, "struct_ref");
       Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq");
       if (!is_empty_table(Table) && !is_empty_table(Table1) && scheme) {
            Table2 = get_extend_table(Table1, &_Struct_Ref_Seq);
            if (Table2) {
                 _CifObj->DeleteTable(_StructureId, _Struct_Ref_Seq.category);
                 _CifObj->WriteTable(Table2, _StructureId, _Struct_Ref_Seq.category);
                 Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq");
            }
            auth_seq_beg = Table1->GetColumnIndex("ndb_auth_seq_align_beg");
            auth_beg_ins = Table1->GetColumnIndex("ndb_seq_align_beg_ins_code");
            auth_seq_end = Table1->GetColumnIndex("ndb_auth_seq_align_end");
            auth_end_ins = Table1->GetColumnIndex("ndb_seq_align_end_ins_code");
            seq_beg = Table1->GetColumnIndex("seq_align_beg");
            seq_end = Table1->GetColumnIndex("seq_align_end");
            chn_idx = Table1->GetColumnIndex("ndb_chain_id");
            rowNo = Table1->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(beg, Table1, seq_beg, i);
                 Table1->UpdateCell(beg, auth_seq_beg, i);
                 get_value(string, Table1,  auth_beg_ins, i);
                 if (string != "") beg += string;
                 get_value(end, Table1, seq_end, i);
                 Table1->UpdateCell(end, auth_seq_end, i);
                 get_value(string, Table1, auth_end_ins, i);
                 if (string != "") end += string;
                 get_value(string, Table1, chn_idx, i);
                 if (beg != "" && find_index(scheme, string.Text(), beg) &&
                     end != "" && find_index(scheme, string.Text(), end)) {
                      Table1->UpdateCell(beg, seq_beg, i);
                      Table1->UpdateCell(end, seq_end, i);
                 } else {
                      ndb_log_message(NDB_MSG_ERR, "%s: can't find CIF index for %s  %s  %s\n",
                           _StructureId, string.Text(), beg.Text(), end.Text());
                 }
            }
            _CifObj->WriteTable(Table1, _StructureId, "struct_ref_seq");
       }

       Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
       if (!is_empty_table(Table) && !is_empty_table(Table1) && scheme) {
            Table2 = get_extend_table(Table1, &_Struct_Ref_Seq_Dif);
            if (Table2) {
                 _CifObj->DeleteTable(_StructureId, _Struct_Ref_Seq_Dif.category);
                 _CifObj->WriteTable(Table2, _StructureId, _Struct_Ref_Seq_Dif.category);
                 Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
            }
            auth_seq_beg = Table1->GetColumnIndex("ndb_auth_seq_num");
            seq_beg = Table1->GetColumnIndex("seq_num");
            chn_idx = Table1->GetColumnIndex("ndb_pdb_chain_id");
            rowNo = Table1->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(beg, Table1, seq_beg, i);
                 Table1->UpdateCell(beg, auth_seq_beg, i);
                 if (beg == "") continue;
                 get_value(string, Table1, chn_idx, i);
                 if (find_index(scheme, string.Text(), beg))
                      Table1->UpdateCell(beg, seq_beg, i);
                 else ndb_log_message(NDB_MSG_ERR, "%s: can't find CIF index for %s  %s\n",
                           _StructureId, string.Text(), beg.Text());
            }
            _CifObj->WriteTable(Table1, _StructureId, "struct_ref_seq_dif");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq");
       Table1 = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
       if (!is_empty_table(Table) && !is_empty_table(Table1)) {
            rowNo = Table->GetNumRows();
            rowNo1 = Table1->GetNumRows();
            align_id = Table->GetColumnIndex("align_id");
            align_id1 = Table1->GetColumnIndex("align_id");
            chn_idx = Table->GetColumnIndex("ndb_chain_id");
            id_idx = Table1->GetColumnIndex("ndb_pdb_chain_id");
            for (i = 0; i < rowNo1; i++) {
                 Table1->GetCell(id, id_idx, i);
                 for (j = 0; j < rowNo; j++) {
                      Table->GetCell(string, chn_idx, j);
                      if (string == id) {
                           Table->GetCell(string, align_id, j);
                           Table1->UpdateCell(string, align_id1, i);
                           break;
                      }
                 }
            }
            _CifObj->WriteTable(Table1, _StructureId, "struct_ref_seq_dif");
       }

       if (scheme) {
            if (scheme->chnid) {
                 for (i = 0; i < scheme->num; i++)
                      if (scheme->chnid[i]) delete [] scheme->chnid[i];
                 delete [] scheme->chnid;
            }
            if (scheme->index) {
                 for (i = 0; i < scheme->num; i++) {
                      if (scheme->index[i]) {
                           if (scheme->index[i]->pair) {
                                for (j = 0; j < scheme->index[i]->length; j++) {
                                     if (scheme->index[i]->pair[j])
                                          delete scheme->index[i]->pair[j];
                                }
                                delete [] scheme->index[i]->pair;
                           }
                           delete scheme->index[i];
                      }
                 }
                 delete [] scheme->index;
            }
            delete scheme;
            scheme = NULL;
       }

       Table = _CifObj->GetTablePtr(_StructureId, "database_PDB_rev");
       if (!is_empty_table(Table)) {
            type_idx = Table->GetColumnIndex("status");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "") continue;
                 string.ToLower();
                 if (string == "y") {
                      string.Copy("full release");
                      Table->UpdateCell(string, type_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "database_PDB_rev");
       }

       for (k = 0; k < NUM_CAT_ITEM_TO_LOWER; k++) {
            Table = _CifObj->GetTablePtr(_StructureId, _cat_items_to_lower[k].category);
            if (is_empty_table(Table)) continue;
            type_idx = Table->GetColumnIndex(_cat_items_to_lower[k].item);
            if (type_idx < 0) continue;

            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string == "") continue;
                 string.ToLower();
                 Table->UpdateCell(string, type_idx, i);
            }
            _CifObj->WriteTable(Table, _StructureId, _cat_items_to_lower[k].category);
       }

       Table = _CifObj->GetTablePtr(_StructureId, "refine_ls_shell");
       if (!is_empty_table(Table)) {
            rowNo = Table->GetNumRows();
            type_idx = Table->GetColumnIndex("ndb_total_number_of_bins_used");
            id_idx = Table->GetColumnIndex("number_reflns_R_free");
            text.Clear();
            for (i = 0; i < rowNo; i++) {
                 get_value(string, Table, type_idx, i);
                 if (string != "") {
                      last = atoi(string.Text());
                      sprintf(buffer, "%d", last);
                      text.Copy(buffer);
                      Table->UpdateCell(text, type_idx, i);
                 } else if (text != "")
                      Table->UpdateCell(text, type_idx, i);
                 get_value(string, Table, id_idx, i);
                 if (string != "") {
                      last = atoi(string.Text());
                      sprintf(buffer, "%d", last);
                      string.Copy(buffer);
                      Table->UpdateCell(string, id_idx, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "refine_ls_shell");
       }

       Table = _CifObj->GetTablePtr(_StructureId, "database_PDB_caveat");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("id");
            rowNo = Table->GetNumRows();
            for (i = 0; i < rowNo; i++) {
                 sprintf(buffer, "%d", i + 1);
                 string.Copy(buffer);
                 Table->UpdateCell(string, id_idx, i);
            }
            _CifObj->WriteTable(Table, _StructureId, "database_PDB_caveat");
       }

       for (i = 0; i < NUM_PDB_INSERT_CODE; i++) {
            Table = _CifObj->GetTablePtr(_StructureId, _pdb_insert_code[i].category);
            if (is_empty_table(Table)) continue;
            rowNo = Table->GetNumRows();
            for (j = 0; j < _pdb_insert_code[i].num_items; j++) {
                 auth_seq_beg = Table->GetColumnIndex(_pdb_insert_code[i].items[2 * j]);
                 auth_beg_ins = Table->GetColumnIndex(_pdb_insert_code[i].items[2*j+1]);
                 if (auth_seq_beg >= 0 && auth_beg_ins >= 0) {
                      for (k = 0; k < rowNo; k++) {
                           Table->GetCell(string, auth_seq_beg, k);
                           Table->GetCell(id, auth_beg_ins, k);
                           if (string != "" && string != "." && string != "?" &&
                               id != "" && id != "." && id != "?") {
                                string += id;
                                Table->UpdateCell(string,  auth_seq_beg, k);
                           }
                      }
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, _pdb_insert_code[i].category);
       }

       for (i = 0; i < NUM_TABLELIST; i++) {
            Table = _CifObj->GetTablePtr(_StructureId, _table_list[i]);
            if (!Table) continue;

            ColumnNames = Table->GetColumnNames();
            colNo = Table->GetNumColumns();
            rowNo = Table->GetNumRows();
            for (j = 0; j < rowNo; j++) {
                 if (is_empty_row(Table, ColumnNames, colNo, j, "entry_id")) {
/*
                      Table->RemoveRow(j);
                      j--; rowNo--;
*/
                      Table->DeleteRow(j);
                 }
            }
            Table->CompressTable();
            if (ColumnNames) delete ColumnNames;

            rowNo = Table->GetNumRows();
            if (!rowNo)
                 _CifObj->DeleteTable(_StructureId, _table_list[i]);
            else _CifObj->WriteTable(Table, _StructureId, _table_list[i]);
       }

       for (i = 0; i < NUM_EM_CATEGORYS; i++) {
            Table = _CifObj->GetTablePtr(_StructureId, em_categories[i].category);
            if (is_empty_table(Table)) continue;

            rowNo = Table->GetNumRows();
	    for (j = 0; j < em_categories[i].NumItems; j++) {
                 id_idx = Table->GetColumnIndex(em_categories[i].items[j].item);
                 if (id_idx < 0) continue;
                 for (k = 0; k < rowNo; k++) {
                      Table->GetCell(string, id_idx, k);
                      if (string != "?" && string != "" && string != ".") continue;
                      if (em_categories[i].items[j].is_entry_id == 1)
                           string.Copy(_StructureId);
                      else if (em_categories[i].items[j].is_entry_id == 2)
                           string.Copy("1");
		      else {
                           sprintf(buffer, "%d", k + 1);
                           string.Copy(buffer);
                      }
                      Table->UpdateCell(string, id_idx, k); 
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, em_categories[i].category);
       }

       Table = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("rcsb_ordinal");
            if (id_idx < 0) {
                 rowNo = Table->GetNumRows();
		 colNo = Table->GetNumColumns();
		 ColumnNames = Table->GetColumnNames();
		 Table1 = new ISTable("struct_ref_seq_dif");
		 for (i = 0; i < colNo; i++) {
                      Table1->AddColumn((*ColumnNames)[i].Text());
                 }
		 if (ColumnNames) delete ColumnNames;
		 Table1->AddColumn("rcsb_ordinal");
		 id_idx = Table1->GetColumnIndex("rcsb_ordinal");
		 for (i = 0; i < rowNo; i++) {
                      Table1->AddRow();
		      for (j = 0; j < colNo; j++) {
                           Table->GetCell(string, j, i);
		           Table1->UpdateCell(string, j, i);
                      }
		      sprintf(buffer, "%d", i+1);
		      string.Copy(buffer);
		      Table1->UpdateCell(string, id_idx, i);
                 }
		 _CifObj->DeleteTable(_StructureId, "struct_ref_seq_dif");
		 _CifObj->WriteTable(Table1, _StructureId, "struct_ref_seq_dif");
            }
       }

       Table = _CifObj->GetTablePtr(_StructureId, "citation_author");
       if (!is_empty_table(Table)) {
            id_idx = Table->GetColumnIndex("ordinal");
            rowNo = Table->GetNumRows();
            if (id_idx < 0) {
		 colNo = Table->GetNumColumns();
		 ColumnNames = Table->GetColumnNames();
		 Table1 = new ISTable("citation_author");
		 for (i = 0; i < colNo; i++) {
                      Table1->AddColumn((*ColumnNames)[i].Text());
                 }
		 if (ColumnNames) delete ColumnNames;
		 Table1->AddColumn("ordinal");
		 id_idx = Table1->GetColumnIndex("ordinal");
		 for (i = 0; i < rowNo; i++) {
                      Table1->AddRow();
		      for (j = 0; j < colNo; j++) {
                           Table->GetCell(string, j, i);
		           Table1->UpdateCell(string, j, i);
                      }
                 }
		 _CifObj->DeleteTable(_StructureId, "citation_author");
                 Table = Table1;
            }
            for (i = 0; i < rowNo; i++) {
		 sprintf(buffer, "%d", i+1);
		 string.Copy(buffer);
		 Table->UpdateCell(string, id_idx, i);
            }
            _CifObj->WriteTable(Table, _StructureId, "citation_author");
       }

       if (standardized) {
            AddMandatoryItem(_CifObj, _experiment_type);
            _CifObj->DeleteTable(_StructureId, "audit_conform");
            Table = new ISTable("audit_conform");
            Table->AddColumn("dict_name");
            Table->AddColumn("dict_version");
            Table->AddColumn("dict_location");
            Table->AddRow();
            string.Copy("cif_mm.dic");
            Table->UpdateCell(string, 0, 0);
            string.Copy("2.0.03");
            Table->UpdateCell(string, 1, 0);
            string.Copy("http://pdb.rutgers.edu/mmcif/dictionaries/ascii/cif_mm.dic");
            Table->UpdateCell(string, 2, 0);
            Table->AddRow();
            string.Copy("cif_pdbx.dic");
            Table->UpdateCell(string, 0, 1);
            string.Copy("0.8.07");
            Table->UpdateCell(string, 1, 1);
            string.Copy("http://pdb.rutgers.edu/mmcif/dictionaries/ascii/pdbx_exchange.dic");
            Table->UpdateCell(string, 2, 1);
            _CifObj->WriteTable(Table, _StructureId, "audit_conform");

            _CifObj->DeleteTable(_StructureId, "audit");
            Table = new ISTable("audit");
            Table->AddColumn("revision_id");
            Table->AddColumn("creation_date");
            Table->AddColumn("update_record");
            Table1 = _CifObj->GetTablePtr(_StructureId, "database_PDB_rev");
            if (is_empty_table(Table1)) {
                 Table->AddRow();
                 id.Copy("1");
                 Table->UpdateCell(id, 0, 0);
                 get_date(buffer);
                 string.Copy(buffer);
                 Table->UpdateCell(string, 1, 0);
                 string.Copy("initial release");
                 Table->UpdateCell(string, 2, 0);
            } else {
                 rowNo = Table1->GetNumRows();
                 id_idx = Table1->GetColumnIndex("num");
                 mon_id = Table1->GetColumnIndex("date");
                 rowNo1 = 0;
                 Table2 = _CifObj->GetTablePtr(_StructureId, "database_PDB_rev_record");
                 if (!is_empty_table(Table2)) {
                      name_idx = Table2->GetColumnIndex("rev_num");
                      type_idx = Table2->GetColumnIndex("type");
                      rowNo1 = Table2->GetNumRows();
                 }
                 for (i = 0; i < rowNo; i++) {
                      Table->AddRow();
                      Table1->GetCell(id, id_idx, i);
                      Table->UpdateCell(id, 0, i);
                      Table1->GetCell(string, mon_id, i);
                      Table->UpdateCell(string, 1, i);
                      if (!i) string.Copy("initial release");
                      else {
                           strcpy(buffer, "adjusted to");
                           for (j = 0; j < rowNo1; j++) {
                                Table2->GetCell(string, name_idx, j);
                                if (string == id) {
                                     Table2->GetCell(string, type_idx, j);
                                     if (string == "HEADER" || string == "KEYWDS") {
                                          if (!strstr(buffer, " struct_keywords category,"))
                                               strcat(buffer, " struct_keywords category,");
                                     } else if (string == "TITLE") {
                                          if (!strstr(buffer, " struct category,"))
                                               strcat(buffer, " struct category,");
                                     } else if (string == "OBSLTE" || string == "SPRSDE") {
                                          if (!strstr(buffer, " pdbx_database_PDB_obs_spr category,"))
                                               strcat(buffer, " pdbx_database_PDB_obs_spr category,");
                                     } else if (string == "SOURCE" || string == "COMPND") {
                                          if (!strstr(buffer, " source and compound information,"))
                                               strcat(buffer, " source and compound information,");
                                     } else if (string == "JRNL") {
                                          if (!strstr(buffer, " citation information,"))
                                               strcat(buffer, " citation information,");
                                     } else if (string == "REMARK") {
                                          if (!strstr(buffer, " citation information,"))
                                               strcat(buffer, " citation information,");
                                     } else if (string == "DBREF" || string == "SEQADV" ||
                                                string == "SEQRES" || string == "MODRES") {
                                          if (!strstr(buffer, " sequence information,"))
                                               strcat(buffer, " sequence information,");
                                     } else if (string == "HET" || string == "HETNAM" ||
                                                string == "HETSYN" || string == "FORMUL") {
                                          if (!strstr(buffer, " chem_comp category,"))
                                               strcat(buffer, " chem_comp category,");
                                     } else if (string == "HELIX" || string == "SHEET" ||
                                                string == "TURN") {
                                          if (!strstr(buffer, " secondary structure information,"))
                                               strcat(buffer, " secondary structure information,");
                                     } else if (string == "SSBOND" || string == "LINK" || string == "CONECT" ||
                                               string == "HYDBND" || string == "SLTBRG") {
                                          if (!strstr(buffer, " connectivity information,"))
                                               strcat(buffer, " connectivity information,");
                                     } else if (string == "CRYST1" || string == "ORIGX" ||
                                                string == "SCALE" || string == "MTRIX" || string == "TVECT") {
                                          if (!strstr(buffer, " crystallographic transformation,"))
                                               strcat(buffer, " crystallographic transformation,");
                                     } else if (string == "ATOM" || "HETATM") {
                                          if (!strstr(buffer, " coordinates,"))
                                               strcat(buffer, " coordinates,");
                                     }
                                }
                           }
                           if (!strcmp(buffer, "adjusted to")) strcpy(buffer, "");
                           else buffer[strlen(buffer)-1] = '.';
                           string.Copy(buffer);
                      }
                      Table->UpdateCell(string, 2, i);
                 }
            }
            _CifObj->WriteTable(Table, _StructureId, "audit");

            _CifObj->DeleteTable(_StructureId, "pdbx_audit");
            Table = new ISTable("pdbx_audit");
            Table->AddColumn("entry_id");
            Table->AddColumn("current_version");
            Table->AddRow();
            string.Copy(_StructureId);
            Table->UpdateCell(string, 0, 0);
            Table->UpdateCell(id, 1, 0);
            _CifObj->WriteTable(Table, _StructureId, "pdbx_audit");

            _CifObj->DeleteTable(_StructureId, "ndb_database_proc");
            if (!keep_contact_author)
                 _CifObj->DeleteTable(_StructureId, "audit_contact_author");
            _CifObj->DeleteTable(_StructureId, "ndb_database_remark");
            _CifObj->DeleteTable(_StructureId, "chem_comp_atom");

            categoryName = _CifObj->GetTableNames(numCategory);
            for (i = 0; i < numCategory; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, categoryName[i]);
                 if (Table) {
                      if (is_empty_table(Table))
                           _CifObj->DeleteTable(_StructureId, categoryName[i]);
                      else if (!strcasecmp(categoryName[i], "entity_name_com") ||
                               !strcasecmp(categoryName[i], "entity_keywords") ||
                               !strcasecmp(categoryName[i], "entity_name_sys")) {
                           rowNo = Table->GetNumRows();
                           colNo = Table->GetNumColumns();
                           id_idx = Table->GetColumnIndex("entity_id");
                           for (j = 0; j < rowNo; j++) {
                                found_gap = 0;
                                for (k = 0; k < colNo; k++) {
                                     Table->GetCell(string, k, j);
                                     if (string != "?" && string != "." &&
                                         string != "" && string != "@") {
                                          if (k == id_idx) continue;
                                          found_gap = 1;
                                          break;
                                     }
                                }
                                if (found_gap) break;
                           }
                           if (j == rowNo) _CifObj->DeleteTable(_StructureId, categoryName[i]);
                      }
                 }
                 delete categoryName[i];
            }
            if (categoryName) delete categoryName;
       }

       categoryName = _CifObj->GetTableNames(numCategory);
       for (i = 0; i < numCategory; i++) {
            Table = _CifObj->GetTablePtr(_StructureId, categoryName[i]);
            if (!is_empty_table(Table) &&
                strcasecmp(categoryName[i], "ndb_pdb_compnd") &&
                strcasecmp(categoryName[i], "ndb_pdb_source") &&
                strcasecmp(categoryName[i], "database_PDB_remark") &&
                strcasecmp(categoryName[i], "ndb_database_remark") &&
                strcasecmp(categoryName[i], "atom_site") &&
                strcasecmp(categoryName[i], "struct_ref") &&
                strcasecmp(categoryName[i], "entity_poly") &&
                strcasecmp(categoryName[i], "atom_site_anisotrop") &&
                strcasecmp(categoryName[i], "ndb_original_ndb_coordinates") &&
                strcasecmp(categoryName[i], "ndb_original_pdb_coordinates") &&
                strcasecmp(categoryName[i], "rcsb_database_PDB_remark")) {
                 colNo = Table->GetNumColumns();
                 rowNo = Table->GetNumRows();
                 for (j = 0; j < rowNo; j++) {
                      for (k = 0; k < colNo; k++) {
                           Table->GetCell(string, k, j);
                           ndb_strip_leading_blanks(string.Text());
                           ndb_strip_trailing_blanks(string.Text());
                           Table->UpdateCell(string, k, j);
                      }
                 }
                 _CifObj->WriteTable(Table, _StructureId, categoryName[i]);
            }
            delete [] categoryName[i];
       }
       if (categoryName) delete [] categoryName;
}

static void remove_infinite(ISTable *Table, const int id_idx)
{
       CifString cifstring;
       get_value(cifstring, Table, id_idx, 0);
       if (strstr(cifstring.Text(), "INFINI")) {
            cifstring.Copy("");
            Table->UpdateCell(cifstring, id_idx, 0);
       }
}

static void remove_comma(ISTable *Table, const int id_idx)
{
       unsigned int i, j;
       char buffer[20];
       CifString cifstring;

       get_value(cifstring, Table, id_idx, 0);
       if (strstr(cifstring.Text(), ",")) {
            j = 0;
            for (i = 0; i < strlen(cifstring.Text()); i++) {
                 if (isdigit(cifstring.Text()[i])) {
                      buffer[j] = cifstring.Text()[i];
                      j++;
                 }
            }
            buffer[j] = '\0';
            cifstring.Copy(buffer);
            Table->UpdateCell(cifstring, id_idx, 0);
       }
}

static void remove_k(ISTable *Table, const int col, const int row)
{
       char buffer[100], *p;
       CifString cifstring;

       get_value(cifstring, Table, col, row);
       if (cifstring != "") {
            strcpy(buffer, cifstring.Text());
            p = strstr(buffer, "K");
            if (p) *p = '\0';
            p = strstr(buffer, "k");
            if (p) *p = '\0';
            cifstring.Copy(buffer);
            Table->UpdateCell(cifstring, col, row);
       }
}

static void update_index(ISTable *Table, const int idx, ISTable *Table1, const int idx1)
{
       int i, j, rowNo, irow;
       CifString cifstring, beg;

       rowNo = Table->GetNumRows();
       for (i = 0; i < rowNo; i++) {
            get_value(cifstring, Table, idx, i);
            if (cifstring == "") continue;
            irow = Table1->GetNumRows();
            for (j = 0; j < irow; j++) {
                 get_value(beg, Table1, idx1, j);
                 if (beg == cifstring) break;
                 if (beg == "") {
                      Table1->UpdateCell(cifstring, idx1, j);
                      break;
                 }
            }
            if (j < irow) continue;

            Table1->AddRow();
            Table1->UpdateCell(cifstring, idx1, irow);
       }
}

static ISTable *get_extend_table(ISTable *Table, _PDB_INSERT_CODE *Miss_Man_I_cat)
{
       int i, j, colNo, rowNo, idx[NUM_ITEM];
       CifString cifstring;
       ISTable *Table1 = NULL;
       ReVarCifArray<CifString> *ColumnNames = NULL;

       for (i = 0; i < Miss_Man_I_cat->num_items; i++) {
            idx[i] = Table->GetColumnIndex(Miss_Man_I_cat->items[i]);
       }
       for (i = 0; i < Miss_Man_I_cat->num_items; i++) {
            if (idx[i] < 0) break;
       }
       if (i == Miss_Man_I_cat->num_items) return NULL;

       ColumnNames = Table->GetColumnNames();
       colNo = Table->GetNumColumns();
       rowNo = Table->GetNumRows();
       Table1 = new ISTable(Miss_Man_I_cat->category);
       for (i = 0; i < colNo; i++)
            Table1->AddColumn((*ColumnNames)[i].Text());
       if (ColumnNames) delete ColumnNames;
       for (i = 0; i < Miss_Man_I_cat->num_items; i++) {
            if (idx[i] < 0) Table1->AddColumn(Miss_Man_I_cat->items[i]);
       }
       for (i = 0; i < rowNo; i++) {
            Table1->AddRow();
            for (j = 0; j < colNo; j++) {
                 Table->GetCell(cifstring, j, i);
                 Table1->UpdateCell(cifstring, j, i);
            }
       }
       return Table1;
}

static int find_index(SCHEME_TABLE *scheme, const char *chnid, CifString &num)
{
       int i, j;
       for (i = 0; i < scheme->num; i++) {
            if (!strcmp(chnid, scheme->chnid[i])) {
                 for (j = 0; j < scheme->index[i]->length; j++) {
                      if (num == scheme->index[i]->pair[j]->pdb) {
                           num.Copy(scheme->index[i]->pair[j]->cif);
                           return 1;
                      }
                 }
            }
       }
       return 0;
}

static void AddMandatoryItem(CifFileObj *in, const int experiment_type)
{
       int i, j, k, l, m, refItemIndex = -1, numBlocks, numTables, errCode;
       int colNo, rowNo,  is_changed = 0, ItemTypeIndex, ItemTypeLIndex;
       int iOut, errCodeRegex, icmp = -1, ret, len;
       char binaryfile[300], **BlockNames = NULL, **TableNames = NULL, keyword[200];
       char *p = NULL, *pm = NULL;
       CifString cifstring, cell, typeCode, patternString;
       DICFileObj *dict = NULL;
       ISTable *refItem = NULL, *Category = NULL, *Table = NULL, *ItemType = NULL, *ItemTypeL = NULL;
       regex_t preg;
       regmatch_t pmatch[NS];
       ReVarCifArray<CifString> refItemList, refItemTarget, MissingItems, *ColumnNames = NULL;
       ReVarCifArray<CifString> ItemTypeLList, *ItemNames = NULL, target, nameList, ItemTypeLTarget;
       ReVarPCifArray<int> *OutList = NULL;
       struct stat stattext;

       if (experiment_type == EXPERIMENT_TYPE_NMR ||
           experiment_type == EXPERIMENT_TYPE_MODEL)
            sprintf(binaryfile, "%s/data/binary/%s", root_dir, dictnmrbinfile);
       else sprintf(binaryfile, "%s/data/binary/%s", root_dir, dictxraybinfile);
       if (stat(binaryfile, &stattext) != -1) dict = new DICFileObj(binaryfile, READ_MODE);
       if (dict == NULL) return;

       nameList.Clear();
       nameList.Add("name");

       refItem = dict->GetTablePtr("item");
       refItemList.Clear();
       refItemList.Add("category_id");
       refItemList.Add("mandatory_code");
       refItem->CreateIndex("index0", refItemList);
       refItemIndex = refItem->GetColumnIndex("name");

       ItemType = dict->GetTablePtr("item_type");
       ItemTypeIndex = ItemType->GetColumnIndex("code");
       ItemTypeL = dict->GetTablePtr("item_type_list");
       ItemTypeLList.Clear();
       ItemTypeLList.Add("code");
       ItemTypeLIndex = ItemTypeL->GetColumnIndex("construct");

       BlockNames = in->GetBlockNames(numBlocks);
       for (i = 0; i < numBlocks; i++) {
            TableNames = in->GetTableNames(BlockNames[i],numTables);
            for (j = 0; j < numTables; j++) {
                 if (!strcmp(TableNames[j], "atom_site") ||
                     !strcmp(TableNames[j], "citation") ||
                     !strcmp(TableNames[j], "atom_site_anisotrop")) {
                      delete [] TableNames[j];
		      continue; 
                 }
                 Category = in->GetTablePtr(BlockNames[i], TableNames[j]);
                 if (is_empty_table(Category)) {
                      delete [] TableNames[j];
                      continue;
		 } 

                 refItemTarget.Clear();
                 refItemTarget.Add(TableNames[j]);
                 refItemTarget.Add("yes");
                 OutList = refItem->Search(refItemTarget, refItemList, errCode);
                 MissingItems.Clear();
                 if (errCode >= 0) {
                      for (k = 0; k < (int) OutList->Length(); k++) {
                           refItem->GetCell(cell, refItemIndex, (*OutList)[k]);
                           GetKeywordFromItemName(keyword,cell.Text());
                           errCode = Category->GetColumnIndex(keyword);
                           if (errCode < 0) {
                                cifstring.Copy(keyword);
                                MissingItems.Add(cifstring);
                           }
                      }
                 }
                 if (OutList) delete OutList; OutList = NULL;

                 is_changed = 0;
                 if (MissingItems.Length() > 0) {
                      Table = Category;
                      ColumnNames = Table->GetColumnNames();
                      colNo = Table->GetNumColumns();
                      rowNo = Table->GetNumRows();
                      Category = new ISTable(TableNames[j]);
                      for (l = 0; l < colNo; l++) Category->AddColumn((*ColumnNames)[l].Text());
                      if (ColumnNames) delete ColumnNames;
                      for (k = 0; k < (int) MissingItems.Length(); k++)
                           Category->AddColumn(MissingItems[k].Text());
                      for (l = 0; l < rowNo; l++) {
                           Category->AddRow();
                           for (m = 0; m < colNo; m++) {
                                Table->GetCell(cifstring, m, l);
                                Category->UpdateCell(cifstring, m, l);
                           }
                      }
                      for (k = 0; k < (int) MissingItems.Length(); k++) {
                           if (MissingItems[k] == "entry_id")
                                cifstring.Copy(BlockNames[i]);
                           else cifstring.Copy(".");
                           for (l = 0; l < rowNo; l++) {
                                Category->UpdateCell(cifstring, colNo + k, l); 
                           }
                      }
                      in->DeleteTable(BlockNames[i], TableNames[j]);         
                      is_changed = 1;
                 }

                 ItemNames = Category->GetItemNames();
                 for (k = 0; k < (int) ItemNames->Length(); k++) {
                      target.Clear();
                      target.Add((*ItemNames)[k]);

                      typeCode.Clear();
                      iOut = ItemType->FindFirst(target, nameList, errCodeRegex);
                      if (errCodeRegex >= 0) ItemType->GetCell(typeCode, ItemTypeIndex, iOut);
                      if (typeCode != "float" && typeCode != "int") continue;

                      ItemTypeLTarget.Clear();
                      ItemTypeLTarget.Add(typeCode);
                      iOut = ItemTypeL->FindFirst(ItemTypeLTarget, ItemTypeLList, errCode);
                      ItemTypeL->GetCell(cell, ItemTypeLIndex, iOut);
                      ConvertEscapedString(cell, patternString); 
                      icmp = regcomp(&preg, patternString.Text(), REG_EXTENDED);
                      regfree(&preg);
                      if (icmp != 0) continue;

                      rowNo = Category->GetNumRows();
                      for (l = 0; l < rowNo; l++){
                           Category->GetCell(cifstring, k, l);
                           if (cifstring == "." || cifstring == "?" || cifstring == "") continue;

                           regcomp(&preg, patternString.Text(), REG_EXTENDED);
                           ret = regexec(&preg, cifstring.Text(), NS, pmatch, 0);
                           regfree(&preg);
                           if (ret != 0) {
                                cifstring.Copy(".");
                                Category->UpdateCell(cifstring, k, l);
                                is_changed = 1;
                           } else { 
                                len = pmatch[0].rm_eo - pmatch[0].rm_so;
                                if (len != (int) strlen(cifstring.Text())) {
                                     p = (char*)malloc(pmatch[0].rm_eo+1);
                                     strncpy(p, cifstring.Text(), pmatch[0].rm_eo); 
                                     p[pmatch[0].rm_eo]='\0';
                                     pm=p+pmatch[0].rm_so;
                                     cifstring.Copy(pm);
                                     Category->UpdateCell(cifstring, k, l);
                                     free (p);
                                     is_changed = 1;
                                }
                           }
                      }
                 }
                 if (ItemNames) delete ItemNames;

                 if (is_changed) in->WriteTable(Category, BlockNames[i], TableNames[j]);

                 if (TableNames[j]) delete[] TableNames[j];
            }
            if (TableNames) delete[] TableNames;
            if (BlockNames[i]) delete[] BlockNames[i];
       }
       if (BlockNames) delete[] BlockNames;
       delete dict;
}

static void GetKeywordFromItemName(char *keyword, const char *itemName)
{
       int i, j, k, ilen;

       strcpy(keyword, "");
       if (!itemName || (ilen = strlen(itemName)) < 3) return;
     
       for (k = 1; k < ilen; k++) {
            if (itemName[k] == '.') break;
       }
       if (k == 1 || k == ilen) return;
       j = 0;
       for (i = k + 1; i < ilen; i++) {
            keyword[j] = itemName[i];
            j++;
       }
       keyword[j] = '\0';
}

static void ConvertEscapedString(CifString& inString, CifString& outString)
{
       int tmpPos = 0;
       unsigned int posI;
       char* tmpStringP = NULL;
       tmpStringP = (char*) malloc(inString.Length() + 1);

       for (posI = 0; posI < inString.Length(); tmpPos++) {
            if ((inString.Text())[posI] == '\\') {
                 switch ((inString.Text())[posI + 1]) {
                      case '\\':
                           tmpStringP[tmpPos] = '\\';
                           posI += 2;
                           break;
                      case 't':
                           tmpStringP[tmpPos] = '\t';
                           posI += 2;
                           break;
                      case 'n':
                           tmpStringP[tmpPos] = '\n';
                           posI += 2;
                           break;
                      default:
                           tmpStringP[tmpPos] = (inString.Text())[posI];
                           posI++;
                           break;
                 }
            } else {
                 tmpStringP[tmpPos] = (inString.Text())[posI];
                 posI++;
            }
       }
       tmpStringP[tmpPos] = '\0';
       outString.Copy(tmpStringP);
       free (tmpStringP);
}
