/*
FILE:     check.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 <stdlib.h>
#include <math.h>
#include <sys/stat.h>

#include "_validation.h"
#include "_seqnum.h"
#include "BTreeObj.h"
#include "check_global.h"
#include "datacheck.h"
#include "DICFileObj.h"
#include "erf_util.h"
#include "util.h"

#define  dictxraybinfile  "mmcif_xray.odb"
#define  dictnmrbinfile   "mmcif_nmr.odb"
#define  taxonomyfile     "taxonomy.txt" 

extern int check_target_sequence(FILE *report, const char *entity_sequence,
         const char *target_filename, const char *entity_id, const char *target_i);

static TblIndexObj TaxnomyList;

static const char *source_category[3] = { "entity_src_gen", "entity_src_nat", "rcsb_entity_src_syn" };

static void check_tokens(_MOLECULE *mol, Cards_Info_Format *CardInfo,
                 const int Num_Token, CHECK_TOKEN_FORMAT *Check_Format,
                 const int skip, const char *file_name, FILE *fp);
static void check_categorys(_MOLECULE *mol, ISTable *Table, CHECK_CATEGORY *category,
                 FILE *fp, const char *file_name, int &count);
static void check_seqnum_redundancy(_MOLECULE *mol, const char *infile_name, FILE *fp, int &count);
static int get_wavelength_list(const char *list);
static int check_taxonomy(const char *name);

void get_taxonomylist(const int taxonomy_flag)
{
       char filename[300], line[300];
       CifString cifstring;
       FILE *fp = NULL;
       struct stat stattext;

       TaxnomyList.Clear();
       if (!taxonomy_flag) return;
       sprintf(filename, "%s/data/ascii/%s", root_dir, taxonomyfile);
       if (stat(filename, &stattext) == 0) {
            fp = fopen(filename, "r");
	    while (!feof(fp)) {
                 fgets(line, 299, fp);
		 if (feof(fp)) break;
		 line[strlen(line)-1] = '\0';
		 cifstring.Copy(line);
		 TaxnomyList.Add(cifstring);
            }
	    fclose (fp);
       }
}

void _VALIDATION::write_check_results(FILE *fp)
{
       int is_ok = 1, index;
       CifString annotator;
       ISTable *Table = NULL;

       annotator.Clear();
       if (_CifObj) Table = _CifObj->GetTablePtr(_StructureId, "ndb_database_status");
       if (!is_empty_table(Table)) {
            index = Table->GetColumnIndex("rcsb_annotator");
            get_value(annotator, Table, index, 0);
       }
       fprintf(fp, "%-11s  %-12s ", annotator.Text(), _StructureId);
       is_ok = 1;
       if (_Error_Check[0]) {
            fprintf(fp, "%d close contact(s), ", _Error_Check[0]);
            is_ok = 0;
       }
       if (_Error_Check[1]) {
            fprintf(fp, "%d bad bond distance(s), ", _Error_Check[1]);
            is_ok = 0;
       }
       if (_Error_Check[2]) {
            fprintf(fp, "%d bad bond angles(s), ", _Error_Check[2]);
            is_ok = 0;
       }
       if (_Error_Check[3]) {
            fprintf(fp, "wrong terminal atom(s), ");
            is_ok = 0;
       }
       if (_Error_Check[4]) {
            fprintf(fp, "sequence(s) coordinates mismatch, ");
            is_ok = 0;
       }
       if (_Error_Check[5]) {
            fprintf(fp, "structural feature(s) coordinates mismatch, ");
            is_ok = 0;
       }
       if (_Error_Check[6]) {
            fprintf(fp, "chirality error, ");
            is_ok = 0;
       }
       if (_Error_Check[7]) {
            fprintf(fp, "space group error, ");
            is_ok = 0;
       }
       if (_Error_Check[8]) {
            fprintf(fp, "Z-value error, ");
            is_ok = 0;
       }
       if (_Error_Check[9]) {
            fprintf(fp, "missing keywords, ");
            is_ok = 0;
       }

       if (_Error_Check[10]) {
            fprintf(fp, "remark 3 or 200 error, ");
            is_ok = 0;
       }
       if (_Error_Check[11]) {
            fprintf(fp, "PDB ID mismatch, ");
            is_ok = 0;
       }
       if (_Error_Check[12]) {
            fprintf(fp, "missing source information, ");
            is_ok = 0;
       }
       if (_Error_Check[13]) {
            fprintf(fp, "PDB Chain ID mismatch, ");
            is_ok = 0;
       }
       if (_Error_Check[14]) {
            fprintf(fp, "DBREF error, ");
            is_ok = 0;
       }
       if (_Error_Check[15]) {
            fprintf(fp, "SEQADV error, ");
            is_ok = 0;
       }
       if (_Error_Check[16]) {
            fprintf(fp, "missing db_name in rcsb_database_related category, ");
            is_ok = 0;
       }
       if (_Error_Check[17]) {
            fprintf(fp, "citation, ");
            is_ok = 0;
       }
       if (_Error_Check[18]) {
            fprintf(fp, "PDB chain ID and number redundancy, ");
            is_ok = 0;
       }
       if (_Error_Check[19]) {
            fprintf(fp, "sequence link, ");
            is_ok = 0;
       }
       if (_Error_Check[21]) {
            fprintf(fp, "extra atom(s), ");
            is_ok = 0;
       }
       if (_Error_Check[22]) {
            fprintf(fp, "ndb_database_proc category, ");
            is_ok = 0;
       }
       if (_Error_Check[23]) {
            fprintf(fp, "mmCIF dictionary compliance ( ");
            if (_Error_Check[23] & 1)
                 fprintf(fp, "missing mandatory item, ");
            if (_Error_Check[23] & 2)
                 fprintf(fp, "duplicate row, ");
            if (_Error_Check[23] & 4)
                 fprintf(fp, "data type error, ");
            if (_Error_Check[23] & 8)
                 fprintf(fp, "out of range, ");
            if (_Error_Check[23] & 16)
                 fprintf(fp, "not defined in enumeration list, ");
            fprintf(fp, "), ");
            is_ok = 0;
       }
       if (_Error_Check[24]) {
            fprintf(fp, "multiple source information, ");
            is_ok = 0;
       }
       if (_Error_Check[25]) {
            fprintf(fp, "atom type error, ");
            is_ok = 0;
       }
       if (_Error_Check[26]) {
            fprintf(fp, "occupancy error, ");
            is_ok = 0;
       }
       if (_Error_Check[27]) {
            fprintf(fp, "same coordinates, ");
            is_ok = 0;
       }
       if (_Error_Check[28]) {
            fprintf(fp, "missing TITLE, ");
            is_ok = 0;
       }
       if (_Error_Check[29]) {
            fprintf(fp, "wrong date (%s ), ", _date_error.Text());
            is_ok = 0;
       }
       if (_Error_Check[30]) {
            fprintf(fp, "MAD experiment, ");
            is_ok = 0;
       }
       if (_Error_Check[31]) {
            fprintf(fp, "wrong alternate location indicator, ");
            is_ok = 0;
       }
       if (_Error_Check[32]) {
            fprintf(fp, "wrong wavelength, ");
            is_ok = 0;
       }
       if (_Error_Check[33]) {
            fprintf(fp, "wrong insertion code, ");
            is_ok = 0;
       }
       if (_Error_Check[34]) {
            fprintf(fp, "large cell dimensions (%s ), ", _large_cell.Text());
            is_ok = 0;
       }
       if (_Error_Check[35]) {
            fprintf(fp, "missing PubMed ID, ");
            is_ok = 0;
       }
       if (_Error_Check[37]) {
            fprintf(fp, "link record(alt. conformation), ");
            is_ok = 0;
       }
       if (_Error_Check[20]) {
            fprintf(fp, "link record(distance), ");
            is_ok = 0;
       }
       if (_Error_Check[36]) {
            fprintf(fp, "link record(other), ");
            is_ok = 0;
       }
       if (_Error_Check[38]) {
            fprintf(fp, "NDB ID problem for NMR nucleic acid structure, ");
            is_ok = 0;
       }
       if (_spell_check != "") {
            fprintf(fp, "misspelled name (%s), ", _spell_check.Text());
	    is_ok = 0;
       }
       if (_error_message != "") {
            if (strstr(_error_message.Text(), "ATOM") ||
                strstr(_error_message.Text(), "HETATM")) {
                 fprintf(fp, "alternate conformation, ");
                 is_ok = 0;
            }
            if (strstr(_error_message.Text(), "Error: Z value is")) {
                 fprintf(fp, "Z-value error, ");
                 is_ok = 0;
            }
       }
       if (_unrecognized_residue != "") {
            fprintf(fp, "Unrecognized residue(s): %s", _unrecognized_residue.Text());
            is_ok = 0;
       }
       if (_Error_Check[39] || _Error_Check[40] || (_Error_Check[41] && _Error_Check[41] != 100)) {
            fprintf(fp, "Target ( ");
	    if (_Error_Check[39]) fprintf(fp, "missing target ID, ");
	    if (_Error_Check[40]) fprintf(fp, "target ID did not exist, ");
	    if (_Error_Check[41] && _Error_Check[41] != 100)
                 fprintf(fp, "equence Identity = %d%% ", _Error_Check[41]);
	    fprintf(fp, "), ");
	    is_ok = 0;
       }
       if (is_ok)
            fprintf(fp, "OK\n");
       else fprintf(fp, "\n");
       fflush(fp);
}

void _VALIDATION::ndb_check_file(FILE *fp, const int Num_Token,
               CHECK_TOKEN_FORMAT *Check_Format, const int Num_Category,
               CHECK_CATEGORY *Check_Category, const int skip, const int public_flag)
{
       int is_error = 0, KEYWDS_TOKEN, TITLE_TOKEN, DTTEMP_TOKEN, i = 0, j = 0, k = 0;
       int l = 0, high_index = 0, low_index = 0, rowNo = 0, src_index = 0, n_center = 0;
       int entity_index = 0, type_index = 0, e_index = 0, s_index = 0, low_similarity;
       int o_index = 0, rowNo1 = 0, rowNo2 = 0, is_idential = 1, index[9];
       int Mol_ID = -1, details, card_id, numRemark, id_idx, pubmed_idx;
       int ndb_pdb_id_code, mon_id, ndb_pdb_chain_id, seq_num, ndb_pdb_ins_code;
       int ndb_seq_db_name, ndb_seq_db_accession_code, db_mon_id, ndb_seq_db_seq_num;
       int ndb_auth_seq_num, ret, ret1, first, len, is_nmr = 0, is_nucleic_acid = 0;
       char SpaceGroup[20], pdb_id[20], buffer[1000], buffer1[200], binaryfile[300];
       char date_original[20], today_date[20], **initial_of_center = NULL;
       char *sequence = NULL, *code = NULL;
       float d_alpha, d_beta, d_gamma;
       double value[9];
       struct stat stattext;
       BTreeObj<_SSIndex> _AsymIndex, _BiolUnit;
       _CHAIN *chain = NULL;
       CifString string, string1, string2, str[2], res_high, res_low, buff;
       ConnectFormat *drug = NULL;
       DICFileObj *dict = NULL;
       ISTable *Table = NULL, *Table1 = NULL, *Table2 = NULL;
       One_Card_Info_Format *pCard = NULL;
       _RESIDUE *residue = NULL;
       _SSIndex idx, idx1, *pidx = NULL;
       TblIndexObj _UniIndex, _BiolIndex;

       if (_num_mol == 0) return;
       if (_input_format != NDB_FILE_FORMAT_PDB &&
           _input_format != NDB_FILE_FORMAT_MMCIF) return;

       if (!_cell->is_artifical) {
            strcpy(SpaceGroup, _cell->space_group);
            is_error = 0;
            if (!strcmp(SpaceGroup, "P 1 2 1") ||
                !strcmp(SpaceGroup, "P 1 21 1") ||
                !strcmp(SpaceGroup, "C 1 2 1")) {
                 d_alpha = fabs(_cell->alpha - 90.0);
                 d_beta  = fabs(_cell->beta - 90.0);
                 d_gamma = fabs(_cell->gamma - 90.0);
                 if (d_alpha > 0.01 || d_beta < 0.01 || d_gamma > 0.01)
                      is_error = 1;
            } else if (!strcmp(SpaceGroup, "P 1 1 2") ||
                       !strcmp(SpaceGroup, "P 1 1 21") ||
                       !strcmp(SpaceGroup, "A 1 1 2")) {
                 d_alpha = fabs(_cell->alpha - 90.0);
                 d_beta  = fabs(_cell->beta - 90.0);
                 d_gamma = fabs(_cell->gamma - 90.0);
                 if (d_alpha > 0.01 || d_beta > 0.01 || d_gamma < 0.01)
                      is_error = 1;
            } else if (!strcmp(SpaceGroup, "H 3") ||
                       !strcmp(SpaceGroup, "H 3 2")) {
                 d_alpha = fabs(_cell->alpha - 90.0);
                 d_beta  = fabs(_cell->beta - 90.0);
                 d_gamma = fabs(_cell->gamma - 120.0);
                 if (d_alpha > 0.01 || d_beta > 0.01 || d_gamma > 0.01)
                      is_error = 1;
            } else if (!strcmp(SpaceGroup, "R 3") ||
                       !strcmp(SpaceGroup, "R 3 2")) {
                 d_alpha = fabs(_cell->alpha - _cell->beta);
                 d_beta  = fabs(_cell->beta - _cell->gamma);
                 if (d_alpha > 0.01 || d_beta > 0.01) is_error = 1;
            }
            if (is_error) {
                 fprintf(fp, "%s: Error: Space Group", _input_filename);
                 fprintf(fp, " '%s', alpha=%.2f beta=%.2f gamma=%.2f\n",
                      SpaceGroup, _cell->alpha, _cell->beta, _cell->gamma);
                 _Error_Check[7]++;
            }
            if ((_cell->a > 250 || _cell->b > 250 || _cell->c > 250) && !public_flag) {
                 fprintf(fp, "%s:  WARNING: Since one (or more) cell dimension is larger than 250 A.\n", _input_filename);
                 fprintf(fp, "               Please check the structure packing with ndbview.\n");
                 fprintf(fp, "               If there is large empty space, ask the author to confirm\n");
                 fprintf(fp, "               the accuracy of cell dimensions in the file.\n");
                 // if (_cell->a > 250) fprintf(fp, "a=%.3f ", _cell->a);
                 // if (_cell->b > 250) fprintf(fp, "b=%.3f ", _cell->b);
                 // if (_cell->c > 250) fprintf(fp, "c=%.3f ", _cell->c);
                 fprintf(fp, "\n");
                 if (_cell->a > 250) {
                      sprintf(buffer, " a=%.3f", _cell->a);
                      _large_cell += buffer;
                 }
                 if (_cell->b > 250) {
                      sprintf(buffer, " b=%.3f", _cell->b);
                      _large_cell += buffer;
                 }
                 if (_cell->c > 250) {
                      sprintf(buffer, " c=%.3f", _cell->c);
                      _large_cell += buffer;
                 }
                 _Error_Check[34]++;
            }
            if (!_cell->z) {
                 fprintf(fp, "%s: Error: Z value\n", _input_filename);
                 _Error_Check[8]++;
            }
       }

       if (_error_message != "") fprintf(fp, "%s\n", _error_message.Text());

       if (_input_format == NDB_FILE_FORMAT_PDB ||
           _input_format == NDB_FILE_FORMAT_MMCIF &&
           _file_status == FILE_STATUS_MMCIF_PLUS_MOL) {
            check_tokens(_molecules[0], _CardInfo, Num_Token, Check_Format,
                    skip, _input_filename, fp);
       } else {
            for (i = 0; i < Num_Category; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, Check_Category[i].category);
                 if (is_empty_table(Table)) continue;
                 check_categorys(_molecules[0], Table, &(Check_Category[i]),
                             fp, _input_filename, _Error_Check[5]);
            }
       }

       for (i = 0; i < _num_mol; i++) {
            check_seqnum_redundancy(_molecules[i], _input_filename, fp, _Error_Check[18]);
       }

       if (_input_format == NDB_FILE_FORMAT_PDB ||
           _input_format == NDB_FILE_FORMAT_NDB) {
            if (!public_flag) {
                 KEYWDS_TOKEN = find_card_id("KEYWDS");
                 pCard = _CardInfo[KEYWDS_TOKEN].Head();
                 if (pCard == NULL || !strcmp(pCard->Field(2), "")) {
                      _Error_Check[9]++;
                      fprintf(fp, "%s: Missing KEYWDS records\n\n", _input_filename);
                 }
                 DTTEMP_TOKEN = find_card_id("DTTEMP");
                 pCard = _CardInfo[DTTEMP_TOKEN].Head();
                 if (pCard && strcmp(pCard->Field(1), "")) {
                      if (atof(pCard->Field(1)) < 0)
                           fprintf(fp, "TEMPERATURE WARNING: %s\n", pCard->Field(1));
                 }
                 TITLE_TOKEN = find_card_id("TITLE");
                 pCard = _CardInfo[TITLE_TOKEN].Head();
                 if (pCard == NULL || !strcmp(pCard->Field(2), "")) {
                      fprintf(fp, "%s: Missing TITLE record\n\n", _input_filename);
                      _Error_Check[28]++;
                 }
            }
       } else {
            Table = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
            if (!is_empty_table(Table) && !public_flag) {
                 details = Table->GetColumnIndex("details");
                 if (details >= 0) {
                      rowNo = Table->GetNumRows();
                      ndb_pdb_id_code = Table->GetColumnIndex("ndb_pdb_id_code");
                      mon_id = Table->GetColumnIndex("mon_id");
                      ndb_pdb_chain_id = Table->GetColumnIndex("ndb_pdb_chain_id");
                      seq_num = Table->GetColumnIndex("seq_num");
                      ndb_pdb_ins_code = Table->GetColumnIndex("ndb_pdb_ins_code");
                      ndb_seq_db_name = Table->GetColumnIndex("ndb_seq_db_name");
                      ndb_seq_db_accession_code = Table->GetColumnIndex("ndb_seq_db_accession_code");
                      db_mon_id = Table->GetColumnIndex("db_mon_id");
                      ndb_seq_db_seq_num = Table->GetColumnIndex("ndb_seq_db_seq_num");
                      ndb_auth_seq_num = Table->GetColumnIndex("ndb_auth_seq_num");

                      card_id = find_card_id("SEQADV");
                      for (i = 0; i < rowNo; i++) {
                           Table->GetCell(string, details, i);
                           if (strcasecmp(string.Text(), "CONFLICT")) continue;

                           pCard = _CardInfo[card_id].add_a_new_card(NdbToken[card_id], 0);
                           pCard->AddElementToField(string.Text(), 10);

                           string.Clear();
                           Table->GetCell(string, ndb_pdb_id_code, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 1);

                           string.Clear();
                           Table->GetCell(string, mon_id, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 2);

                           string.Clear();
                           Table->GetCell(string, ndb_pdb_chain_id, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 3);

                           string.Clear();
                           Table->GetCell(string, seq_num, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 4);

                           string.Clear();
                           Table->GetCell(string, ndb_auth_seq_num, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 4);

                           string.Clear();
                           Table->GetCell(string, ndb_pdb_ins_code, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 5);

                           string.Clear();
                           Table->GetCell(string, ndb_seq_db_name, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 6);

                           string.Clear();
                           Table->GetCell(string, ndb_seq_db_accession_code, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 7);

                           string.Clear();
                           Table->GetCell(string, db_mon_id, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 8);

                           string.Clear();
                           Table->GetCell(string, ndb_seq_db_seq_num, i);
                           if (string != "" && string != "." && string != "?")
                                pCard->AddElementToField(string.Text(), 9);
                      }
                      if (_CardInfo[card_id].Head()) {
                           fprintf(fp, "Please note that the following residues given in your SEQRES conflict\n");
                           fprintf(fp, "with the sequence given in the  database. Do these conflicts represent\n");
                           fprintf(fp, "mutations in the deposited sequence?  Please advise.\n\n");
                           print_general_cards(fp, card_id, NDB_FILE_FORMAT_PDB, numRemark, 0);
                           fprintf(fp, "\n");
                      }
                 }
            }

            for (i = 0; i < NUM_CHECK_RESOLUTION; i++) {
                 Table1 = _CifObj->GetTablePtr(_StructureId, check_res[i].category1);
                 Table2 = _CifObj->GetTablePtr(_StructureId, check_res[i].category2);
                 if (is_empty_table(Table1) || is_empty_table(Table2)) continue;
                 high_index = Table1->GetColumnIndex(check_res[i].item1);
                 low_index  = Table2->GetColumnIndex(check_res[i].item2);
                 if (high_index < 0 || low_index < 0) continue;
                 Table1->GetCell(string, high_index, 0);
                 Table2->GetCell(string1, low_index, 0);
                 if (is_not_empty(string) && is_not_empty(string1)) {
                      if (atof(string.Text()) > atof(string1.Text())) {
                           if (strcmp(check_res[i].message, "")) {
                                if (check_res[i].contain_value)
                                     sprintf(buffer, check_res[i].message, string.Text(), string1.Text());
                                else strcpy(buffer, check_res[i].message);
                                fprintf(fp, "ERROR: %s\n", buffer);
                           } else if (!public_flag) {
                                fprintf(fp, "ERROR: _%s.%s=%s > _%s.%s=%s\n\n",
                                   check_res[i].category1, check_res[i].item1, string.Text(),
                                   check_res[i].category2, check_res[i].item2, string1.Text());
                           }
                           _Error_Check[10]++;
                      }
                 }
            }

            if (_experiment_type == EXPERIMENT_TYPE_XRAY) {
                 for (i = 0; i < NUM_MISSING_VALUE; i++) {
                      Table = _CifObj->GetTablePtr(_StructureId, _Missing_Values[i].category);
                      is_error = 0;
                      if (is_empty_table(Table)) is_error = 1;
                      else {
                           high_index = Table->GetColumnIndex(_Missing_Values[i].item);
                           if (high_index < 0) is_error = 1;
                           else {
                                Table->GetCell(string, high_index, 0);
                                if (!is_not_empty(string)) is_error = 1;
                           }
                      }
                      if (is_error) {
                           if (strcmp(_Missing_Values[i].message, ""))
                                fprintf(fp, "WARNING: %s\n", _Missing_Values[i].message);
                           else if (!public_flag) 
                                fprintf(fp, "WARNING: No _%s.%s value\n\n",
                                     _Missing_Values[i].category, _Missing_Values[i].item);
                      }
                 }
            }

            if (_experiment_type == EXPERIMENT_TYPE_NMR) {
                 Table = _CifObj->GetTablePtr(_StructureId, "rcsb_nmr_software");
                 is_error = 0;
                 if (is_empty_table(Table)) is_error = 1;
                 else {
                      high_index = Table->GetColumnIndex("name");
                      if (high_index < 0) is_error = 1;
                      else {
                           is_error = 1;
                           low_index  = Table->GetColumnIndex("classification");
                           rowNo = Table->GetNumRows();
                           for (i = 0; i < rowNo; i++) {
                                Table->GetCell(string, low_index, i);
                                if (string == "refinement" || string == "solution") {
                                     Table->GetCell(string, high_index, i);
                                     if (is_not_empty(string)) {
                                          is_error = 0;
                                          break;
                                     }
                                }
                           }
                      }
                 }
                 if (is_error) fprintf(fp, "WARNING: Please provide the name of the refinement program.\n\n");

                 Table = _CifObj->GetTablePtr(_StructureId, "rcsb_nmr_ensemble");
                 if (!is_empty_table(Table)) {
                      high_index = Table->GetColumnIndex("conformers_submitted_total_number");
                      if (high_index >= 0) {
                           Table->GetCell(string, high_index, 0);
                           if (is_not_empty(string)) {
                                if (atoi(string.Text()) != _num_mol) {
                                     fprintf(fp, "WARNING: The number %s in ", string.Text());
                                     fprintf(fp, "_rcsb_nmr_ensemble.conformers_submitted_total_number is\n");
                                     fprintf(fp, "         different from the number of deposited conformers %d\n",
                                                    _num_mol);
                                }
                           }
                      }
                 }
            }

            for (i = 0; i < NUM_CATEGORY_COMP; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, _Check_Category_Comp[i].category1);
                 Table1 = _CifObj->GetTablePtr(_StructureId, _Check_Category_Comp[i].category2);
                 if (is_empty_table(Table) || is_empty_table(Table1)) continue;
                 high_index = Table->GetColumnIndex(_Check_Category_Comp[i].item1);
                 low_index  = Table1->GetColumnIndex(_Check_Category_Comp[i].item2);
                 if (high_index < 0 || low_index < 0) continue;
                 Table->GetCell(string, high_index, 0);
                 Table1->GetCell(string1, low_index, 0);
                 if (is_not_empty(string) && is_not_empty(string1)) {
                      if (_Check_Category_Comp[i].op)
                           d_alpha = atof(string.Text()) / atof(string1.Text());
                      else d_alpha = atof(string.Text()) - atof(string1.Text());
                      if (d_alpha > _Check_Category_Comp[i].value) {
                           if (strcmp(_Check_Category_Comp[i].message, "")) {
                                sprintf(buffer, _Check_Category_Comp[i].message, string.Text(),
                                                 string1.Text());
                                fprintf(fp, "WARNING: %s\n", buffer);
                           } else if (!public_flag) {
                                fprintf(fp, "WARNING: The value of ");
                                if (_Check_Category_Comp[i].op)
                                     fprintf(fp, "_%s.%s / _%s.%s ",
                                        _Check_Category_Comp[i].category1, _Check_Category_Comp[i].item1,
                                        _Check_Category_Comp[i].category2, _Check_Category_Comp[i].item2);
                                else fprintf(fp, "_%s.%s - _%s.%s ",
                                        _Check_Category_Comp[i].category1, _Check_Category_Comp[i].item1,
                                        _Check_Category_Comp[i].category2, _Check_Category_Comp[i].item2);
                                fprintf(fp, "is larger than %.1f\n\n", _Check_Category_Comp[i].value);
                           }
                      }
                 }
            }

            for (i = 0; i < NUM_CATEGORY_COMP3; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, _Check_Category_Comp3[i].category);
                 if (is_empty_table(Table)) continue;
                 e_index    = Table->GetColumnIndex(_Check_Category_Comp3[i].item1);
                 high_index = Table->GetColumnIndex(_Check_Category_Comp3[i].item2);
                 low_index  = Table->GetColumnIndex(_Check_Category_Comp3[i].item3);
                 if (e_index < 0 || high_index < 0 || low_index < 0) continue;
                 Table->GetCell(string, e_index, 0);
                 Table->GetCell(string1, high_index, 0);
                 Table->GetCell(string2, low_index, 0);
                 if (is_not_empty(string) && is_not_empty(string1) && is_not_empty(string2)) {
                      if (atof(string.Text()) < 0.000001) {
                           if (fabs(atof(string2.Text()) - atof(string1.Text())) > 0.000001) {
                                if (strcmp(_Check_Category_Comp3[i].message3, "")) {
                                     fprintf(fp, "WARNING: ");
                                     fprintf(fp, _Check_Category_Comp3[i].message3, string1.Text(), string2.Text());
                                } else if (!public_flag) {
                                     fprintf(fp, "WARNING: _%s.%s=%s is not same as _%s.%s=%s\n\n",
                                          _Check_Category_Comp3[i].category, _Check_Category_Comp3[i].item3,
                                          string2.Text(), _Check_Category_Comp3[i].category,
                                          _Check_Category_Comp3[i].item2, string1.Text());
                                }
                           }
                      } else {
                           if (atof(string2.Text()) > atof(string1.Text())) {
                                if (strcmp(_Check_Category_Comp3[i].message1, "")) {
                                     fprintf(fp, "WARNING: %s\n", _Check_Category_Comp3[i].message1);
                                } else if (!public_flag) {
                                     fprintf(fp, "WARNING: _%s.%s=%s > _%s.%s=%s\n\n",
                                          _Check_Category_Comp3[i].category, _Check_Category_Comp3[i].item3,
                                          string2.Text(), _Check_Category_Comp3[i].category,
                                          _Check_Category_Comp3[i].item2, string1.Text());
                                }
                           } else if (fabs(atof(string2.Text()) - atof(string1.Text())) < 0.000001) {
                                if (strcmp(_Check_Category_Comp3[i].message2, "")) {
                                     fprintf(fp, "WARNING: %s\n", _Check_Category_Comp3[i].message2);
                                } else if (!public_flag) {
                                     fprintf(fp, "WARNING: _%s.%s=%s = _%s.%s=%s\n\n",
                                          _Check_Category_Comp3[i].category, _Check_Category_Comp3[i].item3,
                                          string2.Text(), _Check_Category_Comp3[i].category,
                                          _Check_Category_Comp3[i].item2, string1.Text());
                                }
                           }
                      }
                 }
            }

            if (!public_flag)
         {
            is_nucleic_acid = 0;
            Table = _CifObj->GetTablePtr(_StructureId, "entity_poly");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("type");
                 for (i = 0; i < rowNo; i++) {
                      Table->GetCell(string, high_index, i);
                      if (!strcasecmp(string.Text(), "polydeoxyribonucleotide") ||
                          !strcasecmp(string.Text(), "polyribonucleotide")) {
                           is_nucleic_acid = 1;
                           break;
                      }
                 }
            }
            if (is_nucleic_acid && _experiment_type == EXPERIMENT_TYPE_NMR) {
                 Table = _CifObj->GetTablePtr(_StructureId, "database_2");
                 if (!is_empty_table(Table)) {
                      rowNo = Table->GetNumRows();
                      high_index = Table->GetColumnIndex("database_id");
                      low_index  = Table->GetColumnIndex("database_code");
                      string1.Copy("");
                      string2.Copy("");
                      for (i = 0; i < rowNo; i++) {
                           Table->GetCell(string, high_index, i);
                           if (string == "NDB") {
                                Table->GetCell(string1, low_index, i);
                           } else if (string == "PDB") {
                                Table->GetCell(string2, low_index, i);
                           }
                      }
                      if (string1 != string2) {
                           fprintf(fp, "Warning: This NMR structure needs to have its NDB ID changed to match the PDB ID.\n\n");
                           _Error_Check[38]++; 
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "entity_src_gen");
	    if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("ndb_gene_src_scientific_name");
		 low_index  = Table->GetColumnIndex("ndb_host_org_scientific_name");
		 rowNo = Table->GetNumRows();
		 for (i = 0; i < rowNo; i++) {
                      if (high_index >= 0) {
                           Table->GetCell(string, high_index, i);
			   if (string != "" && string != "." && string != "?") {
                                if (check_taxonomy(string.Text())) {
                                     fprintf(fp, "Misspelled name: %s\n", string.Text());
                                     if (_spell_check != "") _spell_check += ", ";
				     _spell_check += string.Text();
                                }
                           }
                      }
                      if (low_index >= 0) {
                           Table->GetCell(string, low_index, i);
			   if (string != "" && string != "." && string != "?") {
                                if (check_taxonomy(string.Text())) {
                                     fprintf(fp, "Misspelled name: %s\n", string.Text());
                                     if (_spell_check != "") _spell_check += ", ";
				     _spell_check += string.Text();
                                }
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "entity_src_nat");
	    if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("ndb_organism_scientific");
		 rowNo = Table->GetNumRows();
		 for (i = 0; i < rowNo; i++) {
                      if (high_index >= 0) {
                           Table->GetCell(string, high_index, i);
			   if (string != "" && string != "." && string != "?") {
                                if (check_taxonomy(string.Text())) {
                                     fprintf(fp, "Misspelled name: %s\n", string.Text());
                                     if (_spell_check != "") _spell_check += ", ";
				     _spell_check += string.Text();
                                }
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "exptl");
            if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("method");
                 Table->GetCell(string, high_index, 0);
                 if (!strcasecmp(string.Text(), "THEORETICAL MODELING") ||
                     !strcasecmp(string.Text(), "THEORETICAL MODELLING")) {
                      fprintf(fp, "WARNING: _exptl.method is '%s'. It should be 'THEORETICAL MODEL'.\n\n",
                                   string.Text());
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "struct_biol_gen");
            Table1 = _CifObj->GetTablePtr(_StructureId, "struct_asym");
            if (!is_empty_table(Table) && !is_empty_table(Table1) &&
                _experiment_type == EXPERIMENT_TYPE_XRAY) {
                 rowNo = Table1->GetNumRows();
                 high_index = Table1->GetColumnIndex("id");
                 low_index = Table1->GetColumnIndex("ndb_type");
                 _UniIndex.Clear();
                 for (i = 0; i < rowNo; i++) {
                      Table1->GetCell(string, low_index, i);
                      if (string == "ATOMP" || string == "ATOMN" || string == "NA" || string == "PR") {
                           Table1->GetCell(string, high_index, i);
                           _UniIndex.Add(string);
                      }
                 }

                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("symmetry");
                 low_index = Table->GetColumnIndex("biol_id");
                 e_index = Table->GetColumnIndex("asym_id");
                 s_index = Table->GetColumnIndex("ndb_new_pdb_asym_id");
                 if (high_index >= 0 && low_index >= 0 && e_index >= 0 && s_index >= 0) {
                      _BiolIndex.Clear();
                      type_index = 0;
                      _AsymIndex.Clear();
                      for (i = 0; i < rowNo; i++) {
                           Table->GetCell(string, e_index, i);
                           if (_UniIndex.Seek(string) < 0) continue;
                           Table->GetCell(string1, low_index, i);
                           if (_BiolIndex.Seek(string1) < 0) _BiolIndex.Add(string1);
                           Table->GetCell(string2, high_index, i);
                           if (string2 != "?" && string2 != "." && string2 != "" &&
                               string2 != "1_555") type_index = 1;
                           if (string2 == "1_555") {
                                Table->GetCell(string1, s_index, i);
                                idx.set_svalue(string.Text());
                                idx.set_ivalue(string1.Text());
                                _AsymIndex.Add(idx);
                           }
                      }

                      if (_BiolIndex.Root() >= 0) {
                           ret = _BiolIndex.GoFirst();
                           if (_BiolIndex.NumElem() == 1 && !type_index) 
                                fprintf(fp, "Please confirm, the biological unit is identical to the asymmetric unit.\n\n");
                           else {
                                if (!type_index)
                                     fprintf(fp, "Please confirm there are %d biological units in the asymmetric unit.\n",
                                                        _BiolIndex.NumElem());
                                else if (_BiolIndex.NumElem() > 1)
                                     fprintf(fp, "Please confirm there are %d biological units.\n", _BiolIndex.NumElem());
                                while (ret >= 0) {
                                     _BiolUnit.Clear();
                                     for (i = 0; i < rowNo; i++) {
                                          Table->GetCell(string, e_index, i);
                                          if (_UniIndex.Seek(string) < 0) continue;
                                          Table->GetCell(string1, low_index, i);
                                          if (string1 != _BiolIndex.GetCurrData()) continue;
                                          Table->GetCell(string1, high_index, i);
                                          idx.set_svalue(string.Text());
                                          _AsymIndex.Seek(idx);
                                          idx = _AsymIndex.GetCurrData();
                                          idx1.set_svalue(idx.ivalue());
                                          if (string1 == "1_555") {
                                               idx1.set_ivalue("");
                                               _BiolUnit.Add(idx1);
                                          }
                                          if (string1 != "?" && string1 != "." && string1 != "" &&
                                              string1 != "1_555") {
                                               _BiolUnit.Seek(idx1);
                                               pidx = _BiolUnit.GetCurrDataPtr();
                                               rcsb_get_symmetry_operation_name(_cell, string1.Text(), buffer);
                                               string2.Copy(pidx->ivalue());
                                               if (string2 != "") string2 += ",";
                                               if ((strlen(string2.Text()) % 80) > 70) string2 += "\n";
                                               // string2 += " ";
                                               string2 += buffer;
                                               string2 += " (";
                                               string2 += string1;
                                               string2 += ")";
                                               pidx->set_ivalue(string2.Text());
                                          }
                                     }
                                     if (_BiolUnit.Root() >= 0) {
                                          if (!type_index) {
                                               fprintf(fp, "      The biological unit %s consists of chain(s) ",
                                                                    _BiolIndex.GetCurrData().Text());
                                               len = strlen(_BiolIndex.GetCurrData().Text()) + 48;
                                          } else if (_BiolIndex.NumElem() > 1) {
                                               fprintf(fp, "      The biological unit %s is generated by ",
                                                                    _BiolIndex.GetCurrData().Text());
                                               len = strlen(_BiolIndex.GetCurrData().Text()) + 43;
                                          } else {
                                               fprintf(fp, "Please confirm that the biological unit is generated by ");
                                               len = 56;
                                          }
                                          _BiolUnit.GoFirst();
                                          ret1 = 1;
                                          first = 1;
                                          while (ret1 >= 0) {
                                               idx = _BiolUnit.GetCurrData();
                                               if (!first) {
                                                    fprintf(fp, ", ");
                                                    len += 2;
                                                    if (type_index) {
                                                         fprintf(fp, "\n       ");
                                                         len = 7;
                                                    }
                                                    if (len > 75) {
                                                         fprintf(fp, "\n");
                                                         len = 0;
                                                    }
                                               }
                                               if (!type_index) {
                                                    fprintf(fp, "%s", idx.svalue());
                                                    len += strlen(idx.svalue());
                                               } else {
                                                    fprintf(fp, "chain %s", idx.svalue());
                                                    len += strlen(idx.svalue()) + 6;
                                               }
                                               if (strcmp(idx.ivalue(), "")) {
                                                    // fprintf(fp, " (x,y,z(1_555),%s)", idx.ivalue());
                                                    fprintf(fp, " (x,y,z (1_555),");
                                                    len += 15;
                                                    if (len > 75) {
                                                         fprintf(fp, "\n");
                                                         len = 0;
                                                    }
                                                    fprintf(fp, "%s)", idx.ivalue());
                                                    len += strlen(idx.ivalue())+1;
                                               }
                                               first = 0;
                                               ret1 = _BiolUnit.GoToNext();
                                          }
                                          fprintf(fp, ".\n");
                                     }
                                     ret = _BiolIndex.GoToNext();
                                }
                                fprintf(fp, "\n");
                           }
                      }
                 }
            }

            _UniIndex.Clear();
            for (i = 0; i < 3; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, source_category[i]);
                 rowNo = 0;
                 if (!is_empty_table(Table)) {
                      rowNo = Table->GetNumRows();
                      entity_index = Table->GetColumnIndex("entity_id");
                      for (j = 0; j < rowNo; j++) {
                           string.Clear();
                           Table->GetCell(string, entity_index, j);
                           if (string != "" && string != "?" && string != ".") {
                                if (_UniIndex.Seek(string) < 0) _UniIndex.Add(string);
                                else {
                                     fprintf(fp, "ERROR: entity '%s' had multiple source information\n\n",
                                                     string.Text());
                                     _Error_Check[24]++;
                                }
                           }
                      }
                 }
            }

            for (i = 0; i < NUM_SAME_VALUE; i++) {
                 Table1 = _CifObj->GetTablePtr(_StructureId, check_same[i].category1);
                 Table2 = _CifObj->GetTablePtr(_StructureId, check_same[i].category2);
                 if (is_empty_table(Table1) || is_empty_table(Table2)) continue;
                 high_index = Table1->GetColumnIndex(check_same[i].item1);
                 low_index  = Table2->GetColumnIndex(check_same[i].item2);
                 if (high_index < 0 || low_index < 0) continue;
                 Table1->GetCell(string, high_index, 0);
                 Table2->GetCell(string1, low_index, 0);
                 if (is_not_empty(string) && is_not_empty(string1) &&
                     fabs(atof(string.Text()) - atof(string1.Text())) > 0.000001) {
                      fprintf(fp, "WARNING: _%s.%s=%s is not same as _%s.%s=%s\n\n",
                            check_same[i].category1, check_same[i].item1, string.Text(),
                            check_same[i].category2, check_same[i].item2, string1.Text());
                 }
            }

            for (i = 0; i < NUM_ZERO_VALUE; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, _Zero_Values[i].category);
                 if (is_empty_table(Table)) continue;
                 high_index = Table->GetColumnIndex(_Zero_Values[i].item);
                 if (high_index < 0) continue;
                 Table->GetCell(string, high_index, 0);
                 if (is_not_empty(string) && atof(string.Text()) < 0.000001) {
                      fprintf(fp, "WARNING: _%s.%s is %s\n\n", _Zero_Values[i].category,
                               _Zero_Values[i].item, string.Text());
                 }
            }

            for (i = 0; i < NUM_CATEGORY_VALUE; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, _Check_Category_Values[i].category);
                 if (is_empty_table(Table)) continue;
                 high_index = Table->GetColumnIndex(_Check_Category_Values[i].item);
                 if (high_index < 0) continue;
                 Table->GetCell(string, high_index, 0);
                 if (is_not_empty(string)) {
                      if (!_Check_Category_Values[i].op &&
                          atof(string.Text()) < _Check_Category_Values[i].value ||
                          _Check_Category_Values[i].op &&
                          atof(string.Text()) > _Check_Category_Values[i].value) {
                           fprintf(fp, "WARNING: The value of _%s.%s '%s' is ",
                                 _Check_Category_Values[i].category,
                                 _Check_Category_Values[i].item, string.Text());
                           if (_Check_Category_Values[i].op)
                                fprintf(fp, "larger than %.1f\n\n", _Check_Category_Values[i].value);
                           else fprintf(fp, "less than %.1f\n\n", _Check_Category_Values[i].value);
                      }
                 }
            }

            for (i = 0; i < NUM_UNIQUE_INDEX; i++) {
                 Table = _CifObj->GetTablePtr(_StructureId, _Unique_Index[i].category);
                 if (is_empty_table(Table)) continue;
                 k = 0;
                 for (j = 0; j < _Unique_Index[i].num_items; j++) {
                      index[j] = Table->GetColumnIndex(_Unique_Index[i].items[j]);
                      if (index[j] >= 0) k = 1;
                 }
                 if (!k) continue;
                 _UniIndex.Clear();
                 rowNo = Table->GetNumRows();
                 for (j = 0; j < rowNo; j++) {
                      string.Copy("");
                      for (k = 0; k < _Unique_Index[i].num_items; k++) {
                           str[k].Copy("");
                           Table->GetCell(str[k], index[k], j);
                           string += "_";
                           string += str[k];
                      }
                      if (_UniIndex.Seek(string) >= 0) {
                           if (_Unique_Index[i].error_flag)
                                fprintf(fp, "ERROR: ");
                           else fprintf(fp, "WARNING: ");
                           fprintf(fp, "repeat value(s)");
                           for (k = 0; k < _Unique_Index[i].num_items; k++) {
                                fprintf(fp, " _%s.%s=%s", _Unique_Index[i].category,
                                        _Unique_Index[i].items[k], str[k].Text());
                           }
                           fprintf(fp, "\n\n");
                      } else _UniIndex.Add(string);
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "diffrn_source");
            if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("rcsb_wavelength");
                 low_index = Table->GetColumnIndex("rcsb_wavelength_list");
                 rowNo = Table->GetNumRows();
                 for (i = 0; i < rowNo; i++) {
                      get_value(string, Table, high_index, i);
                      if (string != "") {
                           len = strlen(string.Text());
                           for (j = 0; j < len; j++) {
                                if (string.Text()[j] != ' ' && string.Text()[j] != '.' &&
                                    string.Text()[j] != ',' && !isdigit(string.Text()[j])) {
                                     fprintf(fp, "WARNING: wrong value '%s' for _diffrn_source.rcsb_wavelength.\n",
                                                string.Text());
                                     _Error_Check[32]++;
                                     break;
                                }
                           }
                      }
                      get_value(string, Table, low_index, i);
                      if (string != "") {
                           len = strlen(string.Text());
                           for (j = 0; j < len; j++) {
                                if (string.Text()[j] != ' ' && string.Text()[j] != '.' &&
                                    string.Text()[j] != ',' && !isdigit(string.Text()[j])) {
                                     fprintf(fp, "WARNING: wrong value '%s' for _diffrn_source.rcsb_wavelength_list.\n",
                                                string.Text());
                                     _Error_Check[32]++;
                                     break;
                                }
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "refine");
            if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("ndb_method_to_determine_struct");
                 get_value(string, Table, high_index, 0);
                 if (!strcasecmp(string.Text(), "MAD")) {
                      Table = _CifObj->GetTablePtr(_StructureId, "diffrn_radiation");
                      if (!is_empty_table(Table) && ((rowNo = Table->GetNumRows()) == 1)) {
                           high_index = Table->GetColumnIndex("rcsb_diffrn_protocol");
                           get_value(string1, Table, high_index, 0);
                           if (!strcasecmp(string1.Text(), "single wavelength")) {
                                _Error_Check[30]++;
                                fprintf(fp, "WARNING: _refine.ndb_method_to_determine_struct is MAD");
                                fprintf(fp, " but _diffrn_radiation.rcsb_diffrn_protocol is ");
                                fprintf(fp, "\"single wavelength\"\n");
                           }
                      }
                      Table = _CifObj->GetTablePtr(_StructureId, "diffrn_source");
                      if (!is_empty_table(Table) && ((rowNo = Table->GetNumRows()) == 1)) {
                           high_index = Table->GetColumnIndex("rcsb_wavelength_list");
                           get_value(string1, Table, high_index, 0);
                           high_index = get_wavelength_list(string1.Text());
                           if (high_index == 0) {
                                _Error_Check[30]++;
                                fprintf(fp, "WARNING: _refine.ndb_method_to_determine_struct is MAD");
                                fprintf(fp, " but there is no value in _diffrn_source.rcsb_wavelength_list\n");
                           } else if (high_index == 1) {
                                _Error_Check[30]++;
                                fprintf(fp, "WARNING: _refine.ndb_method_to_determine_struct is MAD");
                                fprintf(fp, " but there is only %s in _diffrn_source.rcsb_wavelength_list\n", string1.Text());
                                fprintf(fp, "         Please provide all wavelengths used for the MAD technique.\n");
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "struct_keywords");
            if (is_empty_table(Table)) {
                 _Error_Check[9]++;
                 fprintf(fp, "%s: Missing KEYWDS records\n\n", _input_filename);
            } else {
                 high_index = Table->GetColumnIndex("text");
                 if (high_index < 0) {
                      fprintf(fp, "%s: Missing KEYWDS records\n\n", _input_filename);
                      _Error_Check[9]++;
                 } else {
                      Table->GetCell(string, high_index, 0);
                      if (string == "" || string == "." || string == "?" || string == "@") {
                           _Error_Check[9]++;
                           fprintf(fp, "%s: Missing KEYWDS records\n\n", _input_filename);
                      }
                 }
            }
            Table = _CifObj->GetTablePtr(_StructureId, "struct");
            if (is_empty_table(Table)) {
                 _Error_Check[28]++;
                 fprintf(fp, "%s: Missing TITLE record\n\n", _input_filename);
            } else {
                 high_index = Table->GetColumnIndex("title");
                 if (high_index < 0) {
                      fprintf(fp, "%s: Missing TITLE record\n\n", _input_filename);
                      _Error_Check[28]++;
                 } else {
                      Table->GetCell(string, high_index, 0);
                      if (string == "" || string == "." || string == "?" || string == "@") {
                           _Error_Check[28]++;
                           fprintf(fp, "%s: Missing TITLE record\n\n", _input_filename);
                      }
                 }
            }
            Table = _CifObj->GetTablePtr(_StructureId, "ndb_database_proc");
            if (is_empty_table(Table)) {
                 _Error_Check[22]++;
                 fprintf(fp, "%s: No ndb_database_proc category\n\n", _input_filename);
            } else {
                 high_index = Table->GetColumnIndex("cycle_id");
                 low_index = Table->GetColumnIndex("date_end_cycle");
                 if (high_index < 0) {
                      _Error_Check[22]++;
                      fprintf(fp, "%s: Missing cycle_id in ndb_database_proc category\n\n",
                                  _input_filename);
                 } else {
                      rowNo = Table->GetNumRows();
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           get_value(res_low, Table, low_index, i);
                           if (res_high == "1") break;
                      }
                      if (i == rowNo) {
                           _Error_Check[22]++;
                           fprintf(fp, "%s: Missing cycle_id '1' in ndb_database_proc category\n\n",
                                       _input_filename);
                      } else if (res_low == "") {
                           _Error_Check[22]++;
                           fprintf(fp, "%s: Missing date in ndb_database_proc.date_end_cycle for cycle_id '1'\n\n",
                                       _input_filename);
                      }
                      get_date(today_date);
                      high_index = Table->GetColumnIndex("date_begin_cycle");
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           get_value(res_low, Table, low_index, i);
                           if (strcmp(res_high.Text(), res_low.Text()) > 0) {
                                fprintf(fp, "%s: _ndb_database_proc.date_end_cycle '%s' is before _ndb_database_proc.date_begin_cycle '%s'.\n",
                                       _input_filename, res_low.Text(), res_high.Text());
                                sprintf(buffer, " end date %s before begin date %s,",  res_low.Text(), res_high.Text());
                                _date_error += buffer;
                                _Error_Check[29]++;
                           }
                           if (strcmp(res_high.Text(), today_date) > 0) {
                                fprintf(fp, "%s: _ndb_database_proc.date_begin_cycle '%s' is after today '%s'\n",
                                       _input_filename, res_high.Text(), today_date);
                                sprintf(buffer, " future date %s,", res_high.Text());
                                _date_error += buffer;
                                _Error_Check[29]++;
                           }
                           if (strcmp(res_low.Text(), today_date) > 0) {
                                fprintf(fp, "%s: _ndb_database_proc.date_end_cycle '%s' is after today '%s'\n",
                                       _input_filename, res_low.Text(), today_date);
                                sprintf(buffer, " future date %s,", res_low.Text());
                                _date_error += buffer;
                                _Error_Check[29]++;
                           }
                      }
                 }
            }

            pdb_id[0] = '\0';
            Table = _CifObj->GetTablePtr(_StructureId, "database_2");
            if (!is_empty_table(Table)) get_pdb_id(Table, pdb_id);

            Table = _CifObj->GetTablePtr(_StructureId, "database");
            if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("ndb_code_PDB");
                 if (high_index >= 0) {
                      get_value(res_low, Table, high_index, 0);
                      if (strcasecmp(res_low.Text(), pdb_id)) {
                           fprintf(fp, "WARNING: PDB ID '%s' in database category is different", res_low.Text());
                           fprintf(fp, " '%s' from database_2 category\n\n", pdb_id);
                           _Error_Check[11]++;
                      }
                 }
            }

            memset(date_original, 0, 20);
            Table = _CifObj->GetTablePtr(_StructureId, "database_PDB_rev");
            if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("date_original");
                 get_value(res_high, Table, high_index, 0);
                 strcpy(date_original, res_high.Text());
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("num");
                 low_index = Table->GetColumnIndex("replaces");
                 if (high_index >= 0 && low_index >= 0) {
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           get_value(res_low, Table, low_index, i);
                           if (atoi(res_high.Text()) > 0 && strcasecmp(res_low.Text(), pdb_id)) {
                                fprintf(fp, "WARNING: PDB ID '%s' in database_PDB_rev category is different", res_low.Text());
                                fprintf(fp, " '%s' from database_2 category\n\n", pdb_id);
                                _Error_Check[11]++;
                           }
                      }
                 }
            }

            if (strcmp(date_original, "")) {
                 Table = _CifObj->GetTablePtr(_StructureId, "diffrn_detector");
                 if (!is_empty_table(Table)) {
                      high_index = Table->GetColumnIndex("ndb_collection_date");
                      get_value(res_high, Table, high_index, 0);
                      if (res_high != "" && strcmp(res_high.Text(), date_original) > 0) {
                           fprintf(fp, "WARNING: _database_PDB_rev.date_original '%s' is before _diffrn_detector.ndb_collection_date '%s'.\n",
                                   date_original, res_high.Text());
                           sprintf(buffer, " deposit date %s before data collection date %s,", date_original, res_high.Text());
                           _date_error += buffer;
                           _Error_Check[29]++;
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "atom_sites");
            if (!is_empty_table(Table) && !_cell->is_artifical) {
                 index[0] = Table->GetColumnIndex("fract_transf_matrix[1][1]");
                 index[1] = Table->GetColumnIndex("fract_transf_matrix[2][2]");
                 index[2] = Table->GetColumnIndex("fract_transf_matrix[3][3]");
                 index[3] = Table->GetColumnIndex("fract_transf_matrix[1][2]");
                 index[4] = Table->GetColumnIndex("fract_transf_matrix[1][3]");
                 index[5] = Table->GetColumnIndex("fract_transf_matrix[2][1]");
                 index[6] = Table->GetColumnIndex("fract_transf_matrix[2][3]");
                 index[7] = Table->GetColumnIndex("fract_transf_matrix[3][1]");
                 index[8] = Table->GetColumnIndex("fract_transf_matrix[3][2]");
                 e_index = 0;
                 for (i = 0; i < 9; i++) {
                      get_value(res_high, Table, index[i], 0);
                      if (res_high != "") {
                           e_index++;
                           value[i] = atof(res_high.Text());
                      }
                 }
                 if (e_index) {
                      if (e_index < 9)
                           fprintf(fp, "ERROR: An incomplete matrix is present in atom_sites category\n\n");
                      else {
                           is_idential = 1;
                           for (i = 0; i < 3; i++) {
                                if (fabs(value[i] - 1.0) > 0.00001) {
                                     is_idential = 0;
                                     break;
                                }
                           }
                           for (i = 0; i < 3; i++) {
                                if (fabs(value[i]) > 0.00001) {
                                     e_index = 0;
                                     break;
                                }
                           }
                           for (i = 3; i < 9; i++) {
                                if (fabs(value[i]) > 0.00001) {
                                     is_idential = 0;
                                     e_index = 0;
                                     break;
                                }
                           }
                           if (is_idential)
                                fprintf(fp, "ERROR: An identity matrix is present in atom_sites category\n\n");
                           if (e_index)
                                fprintf(fp, "ERROR: All values are zero in atom_sites category\n\n");
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "entity");
            Table1 = _CifObj->GetTablePtr(_StructureId, "struct_asym");
            if (!is_empty_table(Table) && !is_empty_table(Table1)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("id");
                 low_index = Table->GetColumnIndex("type");
                 src_index = Table->GetColumnIndex("src_method");

                 rowNo1 = Table1->GetNumRows();
                 entity_index = Table1->GetColumnIndex("entity_id");
                 type_index = Table1->GetColumnIndex("ndb_type");
                 for (i = 0; i < rowNo; i++) {
                      get_value(res_low, Table, low_index, i);
                      if (!strcasecmp(res_low.Text(), "POLYMER")) {
                           get_value(res_high, Table, high_index, i);
                           for (j = 0; j < rowNo1; j++) {
                                get_value(res_low, Table1, entity_index, j);
                                if (res_high == res_low) {
                                     get_value(res_low, Table1, type_index, j);
                                     if (!strcasecmp(res_low.Text(), "PR") ||
                                         !strcasecmp(res_low.Text(), "ATOMP")) {
                                          get_value(res_low, Table, src_index, i);
                                          if (!strcasecmp(res_low.Text(), "MAN")) {
                                               Table2 = _CifObj->GetTablePtr(_StructureId, "entity_src_gen");
                                               if (!is_empty_table(Table2)) {
                                                    rowNo2 = Table2->GetNumRows();
                                                    e_index = Table2->GetColumnIndex("entity_id");
                                                    s_index = Table2->GetColumnIndex("ndb_gene_src_scientific_name");
                                                    o_index = Table2->GetColumnIndex("ndb_host_org_scientific_name");
                                                    for (k = 0; k < rowNo2; k++) {
                                                         get_value(buff, Table2, e_index, k);
                                                         if (!CompareNoCase(res_high, buff)) {
                                                              string.Copy("");
                                                              string1.Copy("");
                                                              Table2->GetCell(string, s_index, k);
                                                              Table2->GetCell(string1, o_index, k);
                                                              if (string == "" || string == "." || string == "?" ||
                                                                  string == "na" || string == "Na" || string == "NA") {
                                                                   _Error_Check[12]++;
                                                                   fprintf(fp, "WARNING: missing entity_src_gen.ndb_gene_src_scientific_name for entity %s\n\n", res_high.Text());
                                                              }
                                                              if (string1 == "" || string1 == "." || string1 == "?" ||
                                                                  string1 == "na" || string1 == "Na" || string1 == "NA") {
                                                                   _Error_Check[12]++;
                                                                   fprintf(fp, "WARNING: missing entity_src_gen.ndb_host_org_scientific_name for entity %s\n\n", res_high.Text());
                                                              }
                                                              break;
                                                         }
                                                    }
                                                    if (k == rowNo2) {
                                                         _Error_Check[12]++;
                                                         fprintf(fp, "WARNING: missing entity_src_gen information for entity %s\n\n", res_high.Text());
                                                    }
                                               } else {
                                                    fprintf(fp, "WARNING: No entity_src_gen category for entity %s\n\n", res_high.Text());
                                                    _Error_Check[12]++;
                                               }
                                          } else if (!strcasecmp(res_low.Text(), "NAT")) {
                                               Table2 = _CifObj->GetTablePtr(_StructureId, "entity_src_nat");
                                               if (!is_empty_table(Table2)) {
                                                    rowNo2 = Table2->GetNumRows();
                                                    e_index = Table2->GetColumnIndex("entity_id");
                                                    o_index = Table2->GetColumnIndex("ndb_organism_scientific");
                                                    for (k = 0; k < rowNo2; k++) {
                                                         get_value(buff, Table2, e_index, k);
                                                         if (!CompareNoCase(res_high, buff)) {
                                                              string.Copy("");
                                                              Table2->GetCell(string, o_index, k);
                                                              if (string == "" || string == "." || string == "?" ||
                                                                  string == "na" || string == "Na" || string == "NA") {
                                                                   fprintf(fp, "WARNING: missing entity_src_nat.ndb_organism_scientific for entity %s\n\n", res_high.Text());
                                                                   _Error_Check[12]++;
                                                              }
                                                              break;
                                                         }
                                                    }
                                                    if (k == rowNo2) {
                                                         fprintf(fp, "WARNING: missing entity_src_nat information for entity %s\n\n", res_high.Text());
                                                         _Error_Check[12]++;
                                                    }
                                               } else {
                                                    fprintf(fp, "WARNING: No entity_src_nat category for entity %s\n\n", res_high.Text());
                                                    _Error_Check[12]++;
                                               }
                                          } else if (!strcasecmp(res_low.Text(), "SYN")) {
                                               Table2 = _CifObj->GetTablePtr(_StructureId, "rcsb_entity_src_syn");
                                               if (!is_empty_table(Table2)) {
                                                    rowNo2 = Table2->GetNumRows();
                                                    e_index = Table2->GetColumnIndex("entity_id");
                                                    o_index = Table2->GetColumnIndex("details");
                                                    for (k = 0; k < rowNo2; k++) {
                                                         get_value(buff, Table2, e_index, k);
                                                         if (!CompareNoCase(res_high, buff)) {
                                                              string.Copy("");
                                                              Table2->GetCell(string, o_index, k);
                                                              if (string == "" || string == "." || string == "?" ||
                                                                  string == "na" || string == "Na" || string == "NA") {
                                                                   fprintf(fp, "WARNING: missing rcsb_entity_src_syn.details for entity %s\n\n", res_high.Text());
                                                                   _Error_Check[12]++;
                                                              }
                                                              break;
                                                         }
                                                    }
                                                    if (k == rowNo2) {
                                                         _Error_Check[12]++;
                                                         fprintf(fp, "WARNING: missing rcsb_entity_src_syn information for entity %s\n\n", res_high.Text());
                                                    }
                                               } else {
                                                    fprintf(fp, "WARNING: No rcsb_entity_src_syn category for entity %s\n\n", res_high.Text());
                                                    _Error_Check[12]++;
                                               }
                                          }
                                     }
                                     break;
                                }
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "struct_biol_gen");
            if (!is_empty_table(Table) && _num_mol) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("asym_id");
                 low_index = Table->GetColumnIndex("ndb_new_pdb_asym_id");
                 if (high_index >= 0 && low_index >= 0) {
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           get_value(res_low, Table, low_index, i);
                           chain = _molecules[0]->GetFirstChain();
                           while (chain) {
                                if (res_high == chain->ChainID() &&
                                   (chain->chain_type() == ATOMP_TOKEN ||
                                    chain->chain_type() == ATOMN_TOKEN) &&
                                    res_low == "") {
                                     _Error_Check[13]++;
                                     fprintf(fp, "WARNING: NO PDB Chain ID for macromolecule(s)\n\n");
                                }
                                chain = _molecules[0]->GetNextChain();
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "struct_ref");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("db_name");
                 if (high_index >= 0) {
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           if (strcasecmp(res_high.Text(), "PIR") &&
                               strcasecmp(res_high.Text(), "SWS") &&
                               strcasecmp(res_high.Text(), "GB") &&
                               strcasecmp(res_high.Text(), "PRF") &&
                               strcasecmp(res_high.Text(), "EMBL")) {
                                fprintf(fp, "WARNING: db_name=%s in struct_ref category\n\n", res_high.Text());
                                _Error_Check[14]++;
                           }
                      }
                 }
            }

            rowNo1 = 0;
            Table1 = _CifObj->GetTablePtr(_StructureId, "struct_biol_gen");
            if (!is_empty_table(Table1)) {
                 rowNo1 = Table1->GetNumRows();
                 src_index = Table1->GetColumnIndex("ndb_new_pdb_asym_id");
            } else Table1 = NULL;

            Table = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("ndb_chain_id");
                 low_index = Table->GetColumnIndex("ndb_pdb_id_code");
                 if (high_index < 0) fprintf(fp, "WARNING: No PDB Chain ID(s) in struct_ref_seq category\n\n");
                 else if (low_index < 0) fprintf(fp, "WARNING: No PDB ID in struct_ref_seq category\n\n");
                 else {
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           get_value(res_low, Table, low_index, i);
                           if (res_high == "") {
                                _Error_Check[14]++;
                                _Error_Check[13]++;
                                fprintf(fp, "WARNING: No PDB Chain ID(s) in struct_ref_seq category\n\n");
                           } else if (Table1 && rowNo1) {
                                for (j = 0; j < rowNo1; j++) {
                                     Table1->GetCell(string, src_index, j);
                                     if (string == res_high) break;
                                }
                                if (j == rowNo1) {
                                     _Error_Check[14]++;
                                     _Error_Check[13]++;
                                     fprintf(fp, "ERROR: PDB Chain ID(s) mismatch in struct_ref_seq category\n\n");
                                }
                           }
                           if (strcasecmp(res_low.Text(), pdb_id)) {
                                _Error_Check[14]++;
                                _Error_Check[11]++;
                                fprintf(fp, "WARNING: PDB ID '%s' in struct_ref_seq category is different", res_low.Text());
                                fprintf(fp, " '%s' from database_2 category\n\n", pdb_id);
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "struct_ref_seq_dif");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("ndb_pdb_chain_id");
                 low_index = Table->GetColumnIndex("ndb_pdb_id_code");
                 if (high_index < 0) fprintf(fp, "WARNING: No PDB Chain ID(s) in struct_ref_seq_dif category\n\n");
                 else if (low_index < 0) fprintf(fp, "WARNING: No PDB ID in struct_ref_seq_dif category\n\n");
                 else {
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           get_value(res_low, Table, low_index, i);
                           if (res_high == "") {
                                _Error_Check[15]++;
                                _Error_Check[13]++;
                                fprintf(fp, "WARNING: No PDB Chain ID(s) in struct_ref_seq_dif category\n\n");
                           } else if (Table1 && rowNo1) {
                                for (j = 0; j < rowNo1; j++) {
                                     Table1->GetCell(string, src_index, j);
                                     if (string == res_high) break;
                                }
                                if (j == rowNo1) {
                                     _Error_Check[15]++;
                                     _Error_Check[13]++;
                                     fprintf(fp, "ERROR: PDB Chain ID(s) mismatch in struct_ref_seq_dif category\n\n");
                                }
                           } 
                           if (strcasecmp(res_low.Text(), pdb_id)) {
                                _Error_Check[15]++;
                                _Error_Check[11]++;
                                fprintf(fp, "WARNING: PDB ID '%s' in struct_ref_seq_dif category is different", res_low.Text());
                                fprintf(fp, " '%s' from database_2 category\n\n", pdb_id);
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "rcsb_database_related");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("db_name");
                 low_index = Table->GetColumnIndex("db_id");
                 for (i = 0; i < rowNo; i++) {
                      get_value(res_high, Table, high_index, i);
                      get_value(res_low, Table, low_index, i);
                      if (res_low != "" && res_high == "") {
                           fprintf(fp, "WARNING: No DB_NAME for related entry %s in rcsb_database_related category\n\n",
                                          res_low.Text());
                           _Error_Check[16]++;
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "rcsb_missing_residue_list");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 high_index = Table->GetColumnIndex("pdb_chain_id");
                 if (high_index < 0) {
                      fprintf(fp, "WARNING: No PDB Chain ID(s) in rcsb_missing_residue_list category\n\n");
                      _Error_Check[13]++;
                 } else {
                      for (i = 0; i < rowNo; i++) {
                           get_value(res_high, Table, high_index, i);
                           if (res_high == "") {
                                fprintf(fp, "WARNING: No PDB Chain ID(s) in rcsb_missing_residue_list category\n\n");
                                _Error_Check[13]++;
                           }
                      }
                 }
            }

            Table = _CifObj->GetTablePtr(_StructureId, "citation");
            if (!is_empty_table(Table)) {
                 rowNo = Table->GetNumRows();
                 id_idx = Table->GetColumnIndex("id");
                 pubmed_idx = Table->GetColumnIndex("rcsb_database_id_PubMed");
                 high_index = Table->GetColumnIndex("journal_abbrev");
                 low_index = Table->GetColumnIndex("journal_id_ISSN");
                 e_index = Table->GetColumnIndex("journal_volume");
                 s_index = Table->GetColumnIndex("page_first");
                 o_index = Table->GetColumnIndex("page_last");
                 for (i = 0; i < rowNo; i++) {
                      string.Clear();
                      Table->GetCell(string, id_idx, i);
                      if (!strcasecmp(string.Text(), "primary")) {
                           string.Clear();
                           Table->GetCell(string, high_index, i);
                           if (string != "." && string != "" && string != "?" &&
                               strcasecmp(string.Text(), "To be Published") &&
                               strcasecmp(string.Text(), "Unpublished")) {
                                string.Clear();
                                Table->GetCell(string, pubmed_idx, i);
                                if (string == "" || string == "." || string == "?") {
                                     fprintf(fp, "WARNING: No PubMed ID for primary citation\n\n");
                                     _Error_Check[35]++;
                                }
                           }
                      }
                      string.Clear();
                      Table->GetCell(string, high_index, i);
                      if (string != "." && string != "" && string != "?" &&
                          strcasecmp(string.Text(), "To be Published")) {
                           get_value(res_low, Table, low_index, i);
                           if (res_low == "") {
                                fprintf(fp, "WARNING: No ISSN for Journal '%s'\n\n", string.Text());
                                _Error_Check[17]++;
                           }
                      }
                 }
            }

	    Table = _CifObj->GetTablePtr(_StructureId, "pdbx_SG_project");
	    sprintf(buffer, "%s/data/targetdb", root_dir);
	    if (!is_empty_table(Table) && stat(buffer, &stattext) != -1) {
                 high_index = Table->GetColumnIndex("initial_of_center");
		 rowNo = Table->GetNumRows();
		 initial_of_center = new char*[rowNo];
		 for (i = 0; i < rowNo; i++) initial_of_center[i] = NULL;
		 n_center = 0;
		 for (i = 0; i < rowNo; i++) {
                      Table->GetCell(string, high_index, i);
		      if (string == "BSGC" || string == "CESG" || string == "JCSG" ||
                          string == "MCSG" || string =="NYSGRC"|| string == "NESG" ||
			  string == "SECSG"|| string == "SGPP" || string =="TBSGC") {
                           initial_of_center[n_center] = new char[strlen(string.Text())+1];
			   strcpy(initial_of_center[n_center], string.Text());
			   n_center++;
                      }
                 }
		 if (n_center > 0) {
                      Table = _CifObj->GetTablePtr(_StructureId, "entity_poly");
		      if (!is_empty_table(Table)) {
                           high_index = Table->GetColumnIndex("pdbx_target_identifier");
			   if (high_index < 0) _Error_Check[39]++;
			   else {
                                rowNo1 = Table->GetNumRows();
				low_index = Table->GetColumnIndex("ndb_seq_one_letter_code");
				entity_index = Table->GetColumnIndex("entity_id");
				s_index = 0;
				low_similarity = 100;
				for (i = 0; i < rowNo1; i++) {
                                     get_value(string, Table, high_index, i);
				     get_value(string1, Table, low_index, i);
				     get_value(string2, Table, entity_index, i);
				     if (string != "") s_index = 1;
				     if (string != "" && string1 != "") {
                                          string1.ToUpper();
					  ret = strlen(string1.Text());
                                          sequence = new char[ret + 1];
					  memset(sequence, 0, ret + 1);
					  k = 0;
					  for (j = 0; j < ret; j++) {
                                               if (string1.Text()[j] == ' ' ||
                                                   string1.Text()[j] == '\n') continue;
					       if (string1.Text()[j] != '(') {
                                                    sequence[k] = string1.Text()[j];
                                               } else {
                                                    j++;
						    l = 0;
						    while (string1.Text()[j] != ')') {
                                                         buffer[l] = string1.Text()[j];
							 l++; j++;
						    }
						    buffer[l] = '\0';
						    drug = find_drug(buffer);
						    if (drug && drug->parent_residue() &&
                                                        strcmp(drug->parent_residue(), "")) {
                                                         code = get_oneletter_code(drug->parent_residue());
							 if (strlen(code) == 1)
                                                              sequence[k] = code[0];
							 else sequence[k] = 'X';
                                                    } else sequence[k] = 'X';
                                               }
					       k++;
                                          }
					  strcpy(buffer1,  string.Text());
					  ndb_set_string_to_upper(buffer1);
					  for (j = 0; j < n_center; j++) {
					       sprintf(buffer, "%s/data/targetdb/%s_%s", root_dir,
							initial_of_center[j], buffer1); 
					       if (stat(buffer, &stattext) != -1) {
                                                    ret = check_target_sequence(fp, sequence, buffer,
                                                             string2.Text(), string.Text());
						    if (ret > 0) {
                                                         if (ret < low_similarity) low_similarity = ret;
                                                    }
						    break;
                                               }
					  }
					  if (j == n_center) _Error_Check[40]++;
					  delete [] sequence;
                                     }
                                }
				if (!s_index) _Error_Check[39]++;
				if (low_similarity < 100) _Error_Check[41] = low_similarity;
                           }
                      }
                 }
		 for (i = 0; i < rowNo; i++) { if (initial_of_center[i]) delete [] initial_of_center[i]; }
		 delete [] initial_of_center;
            }

            Table = _CifObj->GetTablePtr(_StructureId, "ndb_database_status");
            if (!is_empty_table(Table)) {
                 high_index = Table->GetColumnIndex("author_release_status_code");
                 low_index = Table->GetColumnIndex("date_hold_coordinates");
                 Table->GetCell(string, high_index, 0);
                 Table->GetCell(string1, low_index, 0);
                 if ((string == "HOLD" || string == "hold") &&
                     (string1 == "?" || string1 == "." || string1 == "")) {
                      fprintf(fp, "WARNING: No _ndb_database_status.date_hold_coordinates value ");
                      fprintf(fp, "for _ndb_database_status.author_release_status_code=%s\n\n", string.Text());
                 }
                 high_index = Table->GetColumnIndex("dep_release_code_struct_fact");
                 low_index = Table->GetColumnIndex("date_hold_struct_fact");
                 Table->GetCell(string, high_index, 0);
                 Table->GetCell(string1, low_index, 0);
                 if ((string == "HOLD FOR 6 MONTHS" || string == "HOLD FOR 1 YEAR") &&
                     (string1 == "?" || string1 == "." || string1 == "")) {
                      fprintf(fp, "WARNING: No _ndb_database_status.date_hold_struct_fact value ");
                      fprintf(fp, "for _ndb_database_status.dep_release_code_struct_fact=%s\n\n", string.Text());
                 }
            }

            dict = NULL;
            if (_experiment_type == EXPERIMENT_TYPE_NMR ||
                _experiment_type == EXPERIMENT_TYPE_MODEL) {
                 sprintf(binaryfile, "%s/data/binary/%s", root_dir, dictnmrbinfile);
                 is_nmr = 1;
            } else {
                 sprintf(binaryfile, "%s/data/binary/%s", root_dir, dictxraybinfile);
                 is_nmr = 0;
            }
            if (stat(binaryfile, &stattext) != -1) {
                 dict = new DICFileObj(binaryfile, READ_MODE);
            }
            if (dict) {
                 _Error_Check[23] = DictionaryDataChecking(_CifObj, dict, fp, is_nmr, _Error_Check[29], _date_error);
                 delete dict;
                 dict = NULL;
            }
         }
       }

       string.Clear();
       string1.Clear();
       string2.Clear();
       buff.Clear();
       res_high.Clear();
       for (i = 0; i < _num_mol; i++) {
            if (_num_mol > 1) Mol_ID = _molecules[i]->Mol_ID();
            chain = _molecules[i]->GetFirstChain();
            while (chain) {
                 residue = chain->GetFirstResidue();
                 while (residue) {
                      residue->find_atom_type_mismatch(Mol_ID, string);
                      residue->find_wrong_alt_id(string2);
                      if (_experiment_type != EXPERIMENT_TYPE_NMR)
                           residue->check_occupancy(string1);
                      residue->check_same_coordinates(buff);
                      residue->check_insertion_code(res_high);
                      residue = chain->GetNextResidue();
                 }
                 chain = _molecules[i]->GetNextChain();
            }
       }
       if (string != "") {
            fprintf(fp, "\n==> The following atoms have wrong atom type\n\n");
            fprintf(fp, "     RES MOD#C SEQ          ATOMS    TYPE    TYPE FROM COMPONENT.CIF\n\n");
            fprintf(fp, "%s\n\n", string.Text());
            _Error_Check[25]++;
       }
       if (string1 != "") {
            fprintf(fp, "%s\n\n", string1.Text());
            _Error_Check[26]++;
       }
       if (buff != "") {
            fprintf(fp, "%s\n\n", buff.Text());
            _Error_Check[27]++;
       }
       if (string2 != "") {
            fprintf(fp, "%s\n\n", string2.Text());
            _Error_Check[31]++;
       }
       if (res_high != "") {
            fprintf(fp, "%s\n\n", res_high.Text());
            _Error_Check[33]++;
       }
}

static void check_tokens(_MOLECULE *mol, Cards_Info_Format *CardInfo,
                const int Num_Token, CHECK_TOKEN_FORMAT *Check_Format,
                const int skip, const char *file_name, FILE *fp)
{
       int i , j, tokenid, num;
       char AtomName[10], ChainId[10], ResidueName[10], ResidueNo[10],
              InsCode[10], buffer[200];
       _ATOM **atom = NULL;
       _RESIDUE *residue = NULL;
       One_Card_Info_Format *pCard = NULL;

       for (i = 0; i < Num_Token; i++) {
            if (skip && (!strcmp(Check_Format[i].TokenName, "HET") ||
                         !strcmp(Check_Format[i].TokenName, "CISPEP")))
                 continue;
            tokenid = Check_Format[i].Token_ID;
            if (tokenid == -1 || tokenid == DBREF_TOKEN - 1) continue;

            pCard = CardInfo[tokenid].Head();
            while (pCard != NULL) {
                 for (j = 0; j < Check_Format[i].NumField; j++) {
                      AtomName[0] = ChainId[0] = ResidueName[0] =
                            ResidueNo[0] = InsCode[0] = '\0';
                      if (Check_Format[i].Fields[j].AtmName) strcpy(AtomName,
                            pCard->Field(Check_Format[i].Fields[j].AtmName));
                      if (Check_Format[i].Fields[j].ChainId) strcpy(ChainId,
                            pCard->Field(Check_Format[i].Fields[j].ChainId));
                      if (Check_Format[i].Fields[j].ResName) strcpy(ResidueName,
                            pCard->Field(Check_Format[i].Fields[j].ResName));
                      if (Check_Format[i].Fields[j].ResNum) strcpy(ResidueNo,
                            pCard->Field(Check_Format[i].Fields[j].ResNum));
                      if (Check_Format[i].Fields[j].InsCode) strcpy(InsCode,
                            pCard->Field(Check_Format[i].Fields[j].InsCode));

                      if (!strcmp(AtomName, "") && !strcmp(ResidueName, "") &&
                          !strcmp(ChainId, "") && !strcmp(ResidueNo, "") &&
                          !strcmp(InsCode, "")) continue;

                      buffer[0] = '\0';
                      if (strcmp(AtomName, "")) {
                           atom = mol->find_pdb_atom(AtomName, ChainId,
                                        ResidueName, atoi(ResidueNo), InsCode, num);
                           if (!atom) {
                                if (!strcmp(ChainId, "")) strcpy(ChainId, " ");
                                if (strcmp(InsCode, ""))
                                     sprintf(buffer, "ATOM: %s RES: %s CHNID: %s SSEQ: %s%s",
                                         AtomName, ResidueName, ChainId, ResidueNo, InsCode);
                                else sprintf(buffer, "ATOM: %s RES: %s CHNID: %s SSEQ: %s",
                                         AtomName, ResidueName, ChainId, ResidueNo);
                           } else delete [] atom;
                      } else {
                           residue = mol->find_pdb_residue(ChainId,
                                        ResidueName, atoi(ResidueNo), InsCode);
                           if (!residue) {
                                if (!strcmp(ChainId, "")) strcpy(ChainId, " ");
                                if (strcmp(InsCode, "") &&
                                    strcmp(ResidueName, ""))
                                     sprintf(buffer, "RES: %s CHNID: %s SSEQ: %s%s",
                                         ResidueName, ChainId, ResidueNo, InsCode);
                                else if (strcmp(InsCode, ""))
                                     sprintf(buffer, "CHNID: %s SSEQ: %s%s", ChainId,
                                         ResidueNo, InsCode);
                                else if (strcmp(ResidueName, ""))
                                     sprintf(buffer, "RES: %s CHNID: %s SSEQ: %s",
                                         ResidueName, ChainId, ResidueNo);
                                else sprintf(buffer, "CHNID: %s SSEQ: %s", ChainId,
                                         ResidueNo);
                           }
                      }
                      if (strcmp(buffer, "")) {
                           fprintf(fp, "%s: Error: Record (%s) in Token '%s'",
                             file_name, buffer, NdbToken[tokenid].TokenName);
                           fprintf(fp, " can not be found in coordinates\n");
                      }
                 }
                 pCard = pCard->Next();
            }
       }
}

static void check_categorys(_MOLECULE *mol, ISTable *Table, CHECK_CATEGORY *category,
                 FILE *fp, const char *file_name, int &count)
{
       int index[4][5], i, j, k, rowNo, is_useful, num;
       char value[5][15], buffer[200];
       CifString cifstring, record;
       _ATOM **atom = NULL;
       _RESIDUE *residue = NULL;

       for (i = 0; i < 4; i++) {
            for (j = 0; j < 5; j++) index[i][j] = -1;
       }
       category->type_index = Table->GetColumnIndex(category->type);
       for (i = 0; i < category->NumRecord; i++) {
            if (strcmp(category->items[i].atom_label, ""))
                 index[i][0] = Table->GetColumnIndex(category->items[i].atom_label);
            if (strcmp(category->items[i].asym_label, ""))
                 index[i][1] = Table->GetColumnIndex(category->items[i].asym_label);
            if (strcmp(category->items[i].comp_label, ""))
                 index[i][2] = Table->GetColumnIndex(category->items[i].comp_label);
            if (strcmp(category->items[i].seq_label, ""))
                 index[i][3] = Table->GetColumnIndex(category->items[i].seq_label);
            if (strcmp(category->items[i].ins_label, ""))
                 index[i][4] = Table->GetColumnIndex(category->items[i].ins_label);
       }

       rowNo = Table->GetNumRows();
       record.Copy(category->type);
       for (i = 0; i < rowNo; i++) {
            if (category->type_index >= 0) {
                 is_useful = 0;
                 get_value(record, Table, category->type_index, i);
                 if (!strcmp(category->category, "struct_conf")) {
                      if (record == "HELIX")
                           is_useful = 1;
                      else if (record == "TURN")
                           is_useful = 1;
                 } else if (!strcmp(category->category, "struct_conn")) {
                      if (record == "hydbnd") {
                           record.Copy("HYDBND");
                           is_useful = 1;
                      } else if (record == "disulf") {
                           record.Copy("SSBOND");
                           is_useful = 1;
                      } else if (record == "link") {
                           record.Copy("LINK");
                           is_useful = 1;
                      } else if (record == "sltbrg") {
                           record.Copy("SLTBRG");
                           is_useful = 1;
                      } else if (record == "modres") {
                           record.Copy("MODRES");
                           is_useful = 1;
                      }
                 }
                 if (!is_useful) continue;
            }
            for (j = 0; j < category->NumRecord; j++) {
                 buffer[0] = '\0';
                 for (k = 0; k < 5; k++) {
                      memset(value[k], '\0', 15);
                      if (index[j][k] >= 0) {
                           get_value(cifstring, Table, index[j][k], i);
                           strcpy(value[k], cifstring.Text());
                      }
                 }
                 if (strcmp(value[0], "") || strcmp(value[1], "") || strcmp(value[2], "") ||
                     strcmp(value[3], "") || strcmp(value[4], "")) {
                      if (strcmp(value[0], "")) {
                           atom = mol->find_pdb_atom(value[0], value[1],
                                     value[2], atoi(value[3]), value[4], num);
                           if (!atom) {
                                if (!strcmp(value[1], "")) strcpy(value[1], " ");
                                if (!strcmp(value[4], "")) strcpy(value[4], " ");
                                sprintf(buffer, "ATOM: %s RES: %s CHNID: %s SSEQ: %s%s",
                                          value[0], value[2], value[1], value[3], value[4]);
                           } else delete [] atom;
                      } else {
                           residue = mol->find_pdb_residue(value[1],
                                     value[2], atoi(value[3]), value[4]);
                           if (!residue) {
                                if (!strcmp(value[1], "")) strcpy(value[1], " ");
                                if (!strcmp(value[4], "")) strcpy(value[4], " ");
                                sprintf(buffer, "RES: %s CHNID: %s SSEQ: %s%s",
                                          value[2], value[1], value[3], value[4]);
                           }
                      }
                 }
                 if (strcmp(buffer, "")) {
                      fprintf(fp, "%s: Error: Record (%s) in Token '%s'", file_name,
                                 buffer, record.Text());
                      fprintf(fp, " can not be found in coordinates\n");
                      count++;
                 }
            }
       }
}

static void check_seqnum_redundancy(_MOLECULE *mol, const char *infile_name, FILE *fp, int &count)
{
       int j, first, end, ret;
       BTreeObj<_SEQNUM> SeqNum;
       _CHAIN *chain = NULL;
       _SEQNUM seqnum, tmp;
       char buffer[100];

       SeqNum.Clear();
       chain = mol->GetFirstChain();
       while (chain) {
            for (j = 0; j < chain->SeqLen(); j++) {
                 if (chain->SeqRes(j)->Deleted) continue;
                 if (!chain->SeqRes(j)->Residue) continue;

                 sprintf(buffer, "%s%s", chain->SeqRes(j)->Field[4],
                             chain->SeqRes(j)->InsCode);
                 seqnum.set_chnid(chain->PDB_ChainID());
                 seqnum.set_seqnum(buffer);
                 seqnum.set_name(chain->SeqRes(j)->Field[3]);
                 SeqNum.Add(seqnum);
            }
            chain = mol->GetNextChain();
       }

       if (SeqNum.Root() >= 0) {
            SeqNum.GoFirst();
            tmp = SeqNum.GetCurrData();
            first = 1;
            end = 0;
            ret = SeqNum.GoToNext();
            while (ret >= 0) {
                 seqnum = SeqNum.GetCurrData();
                 if (tmp == seqnum) {
                      if (first) {
                           end = 1;
                           fprintf(fp, "%s: Error: Residues ( ", infile_name);
                           fprintf(fp, "%s %s ", tmp.name(), seqnum.name());
                           first = 0;
                      } else fprintf(fp, "%s ", seqnum.name());
                 } else {
                      if (end) {
                           fprintf(fp, ") have same chain_ID '%s' and residue number '%s'\n",
                           tmp.chnid(), tmp.seqnum());
                           count++;
                      }
                      tmp = seqnum;
                      first = 1;
                      end = 0;
                 }
                 ret = SeqNum.GoToNext();
            }
            if (end) {
                 fprintf(fp, ") have same chain_ID '%s' and residue number '%s'\n",
                           tmp.chnid(), tmp.seqnum());
                 count++;
            }
       }
}

static int get_wavelength_list(const char *list)
{
       int i, count = 0, len;
       char buffer[1000], *p = NULL, buff[100];

       if (list == NULL || !strcmp(list, "")) return 0;

       strcpy(buffer, list);
       len = strlen(buffer);
       for (i = 0; i < len; i++) {
            if (buffer[i] == ',' || buffer[i] == ';') buffer[i] = ' ';
       }
       p = buffer;
       while (p) {
            p = get_word(p, buff);
            if (strcmp(buff, "")) count++;
       }
       return count;
}

static int check_taxonomy(const char *name)
{
       char *buffer = NULL;
       CifString cifstring;

       if (TaxnomyList.NumElem() > 0) {
            buffer = new char[strlen(name)+1];
	    strcpy(buffer, name);
	    ndb_set_string_to_upper(buffer);
	    cifstring.Copy(buffer);
	    delete [] buffer;
	    if (TaxnomyList.Seek(cifstring) >= 0)
                 return 0;
	    else return 1;
       }
       return 0;
}
