#       Copyright Hunter Moseley, 2000. All rights reserved.
#       Written by Hunter Moseley 8/1/2000
#       "Mostly" ReWritten by Gurmukh Sahota 02/01/2001
#	Modified by Hunter Moseley 6/12/2001
#       Modified by Gurmukh Sahota 06/18/2001
#                fixed the cmap_conversion_hash (some wrong names)
#       Modified by Gurmukh Sahota 07/01/2001
#                added hack for name_array where inconsistent with current residue shifts (write_bmrb_file)
#                added hack for non-existant _Atom_type (using first letter of shift name) (write_bmrb_file)
#                modified the multiplicity hash for increased atomType inclusions and some typo's.
#                modified deMultiplicate so that it now accepts -1 residues and demultiplicates accordingly.
#
#	90% Rewritten by Hunter Moseley 11/17/2001
#       Copyright Hunter Moseley, 2001. All rights reserved.
#	Modified by Hunter Moseley 3/18/2002 (800 more lines).
#       Copyright Gurmukh Sahota, 2002. All rights reserved.
#	Modified by Gurmukh Sahota 05/13/2002.  Adding coupling_constant and secondary_structure saveframes
#                     and fixed some "exists" bugs and a last in the monomeric_polymer
#
#  BMRBParsing.pm
#	Contains subroutines for reading and writing BMRB files.
#
#	Subroutines:
#		read_bmrb_file - reads a bmrb file and returns a hash of records (Residue is a hash).
#		write_bmrb_file - prints a bmrb file out to $filename
#		convert_bmrb_shift_names - converts the bmrb shift names using the given shift_conversion_hash.
#		adjust_by_reference - Adjusts the carbon chemical shifts to DSS standard based on their current reference.
#		convert_residue_names - Converts residue names in the bmrb file structure from 3-letter to 1-letter and visa versa
#
package BMRBParsing;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw(clone read_bmrb_file write_bmrb_file adjust_by_reference convert_bmrb_shift_names convert_residue_names readBMRBasCMAP deMultiplicate convertBMRBtoCMap update_shift_ids convertAtomNameBMRBtoCMap convertSSelemBMRBtoCMap convertJBMRBtoCMap residueNameofIndex);
%EXPORT_TAGS = ( ALL => [@EXPORT_OK] );

use strict;
#use Dumpvalue qw(:ALL);
#my $dumper    = new Dumpvalue;


# useful hashes
my %multiplicity = ("A" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 1, "CA" => 1, "CB" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "C" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "CA" => 1, "CB" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "SG" => 1, "HG" => 1},
		    "D" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 1, "HD2" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "OD" => 2, "OD1" => 1, "OD2" => 1},
		    "E" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 2, "HG2" => 1, "HG3" => 1, "HE" => 1, "HE2" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "OE" => 2, "OE1" => 1, "OE2" => 1},
		    "F" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 2, "HD1" => 1, "HD2" => 1, "HE" => 2, "HE1" => 1, "HE2" => 1, "HZ" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 2, "CE" => 2, "CZ" => 1, "CD1" => 1, "CD2" => 1, "CE1" => 1, "CE2" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "G" => { "H" => 1, "HN" => 1, "HA2" => 1, "HA3" => 1, "CA" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "H" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 2, "HD1" => 1, "HD2" => 1, "HE" => 2, "HE1" => 1, "HE2" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "CE" => 1, "CD2" => 1, "CE1" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "ND" => 1, "ND1" => 1, "NE" => 1, "NE2" => 1},
		    "I" => {"H" => 1, "HN" => 1, "HA" => 1, "HB" => 1, "HG" => 3, "HG12" => 1, "HG13" => 1, "HG2" => 1, "HD" => 1, "HD1" => 1, "CA" => 1, "CB" => 1, "CG" => 2, "CD" => 1, "CG1" => 1, "CG2" => 1, "CD1" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "K" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 2, "HG2" => 1, "HG3" => 1, "HD" => 2, "HD2" => 1, "HD3" => 1, "HE" => 2, "HE2" => 1, "HE3" => 1, "HZ" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "CE" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "NZ" => 1},
		    "L" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 2, "HD1" => 1, "HD2" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 2, "CE" => 1, "CD1" => 1, "CD2" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "M" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 1, "HG2" => 1, "HG3" => 1, "HE" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "CE" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "SD" => 1},
		    "N" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 2, "HD2" => 2, "HD21" => 1, "HD22" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CE" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "ND" => 1, "ND2" => 1, "OD" => 1, "OD1" => 1},
		    "P" => { "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 2, "HG2" => 1, "HG3" => 1, "HD" => 2, "HD2" => 1, "HD3" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "Q" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 2, "HG2" => 1, "HG3" => 1, "HE" => 2, "HE2" => 2, "HE21" => 1, "HE22" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "NE" =>1, "NE2" => 1, "OE" => 1, "OE1" => 1},
		    "R" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 2, "HG2" => 1, "HG3" => 1, "HD" => 2, "HD2" => 1, "HD3" => 1, "HE" => 1, "HH" => 4, "HH1" => 2, "HH11" => 1, "HH12" => 1, "HH2" => 2, "HH21" => 1, "HH22" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 1, "CZ" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "NE" => 1, "NH" => 2, "NH1" => 1, "NH2" => 1},
		    "S" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HG" => 1, "CA" => 1, "CB" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "OG" => 1},
		    "T" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 1, "HG" =>2, "HG1" => 1, "HG2" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CG2" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "OG" => 1, "OG1" => 1},
		    "V" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 1, "HG" => 2, "HG1" => 1, "HG2" => 1, "CA" => 1, "CB" => 1, "CG" => 2, "CG1" => 1, "CG2" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1},
		    "W" => { "H" => 1, "HN" => 1, "HA" => 1, "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 1, "HD1" => 1, "HE" => 2, "HE1" => 1, "HE3" => 1, "HZ" => 2, "HZ2" => 1, "HZ3" => 1, "HH" => 1, "HH2" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 2, "CE" => 2, "CZ" => 2, "CH" => 1, "CD1" => 1, "CD2" => 1, "CE2" => 1, "CE3" => 1, "CZ2" => 1, "CZ3" => 1, "CH2" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "NE" => 1, "NE1" => 1},
		    "Y" => { "H" => 1, "HN" => 1, "HA" => 1,  "HB" => 2, "HB2" => 1, "HB3" => 1, "HD" => 2, "HD1" => 1, "HD2" => 1, "HE" => 2, "HE1" => 1, "HE2" => 1, "HH" => 1, "CA" => 1, "CB" => 1, "CG" => 1, "CD" => 2, "CE" => 2, "CZ" => 1, "CD1" => 1, "CD2" => 1, "CE1" => 1, "CE2" => 1, "C" => 1, "CO" => 1, "N" => 1, "N15" => 1, "OH" => 1}
		    );

my %aa_name_conversion = ( "ala" => "A", "arg" => "R", "asn" => "N", "asp" => "D",
			   "cys" => "C", "gln" => "Q", "glu" => "E", "gly" => "G",
			   "his" => "H", "ile" => "I", "leu" => "L", "lys" => "K",
			   "met" => "M", "phe" => "F", "pro" => "P", "ser" => "S",
			   "thr" => "T", "trp" => "W", "tyr" => "Y", "val" => "V",
			   "a"   => "A", "r"   => "R", "n"   => "N", "d"   => "D", 
			   "c"   => "C", "q"   => "Q", "e"   => "E", "g"   => "G",
			   "h"   => "H", "i"   => "I", "l"   => "L", "k"   => "K",
			   "m"   => "M", "f"   => "F", "p"   => "P", "s"   => "S",
			   "t"   => "T", "w"   => "W", "y"   => "Y", "v"   => "V" ); 

my %aa_name_conversion2 = ("A"   => "Ala", "R"   => "Arg", "N"   => "Asn", "D"   => "Asp",
			   "C"   => "Cys", "Q"   => "Gln", "E"   => "Glu", "G"   => "Gly",
			   "H"   => "His", "I"   => "Ile", "L"   => "Leu", "K"   => "Lys",
			   "M"   => "Met", "F"   => "Phe", "P"   => "Pro", "S"   => "Ser",
			   "T"   => "Thr", "W"   => "Trp", "Y"   => "Tyr", "V"   => "Val",
			   "ALA" => "Ala", "ARG" => "Arg", "ASN" => "Asn", "ASP" => "Asp",
			   "CYS" => "Cys", "GLN" => "Gln", "GLU" => "Glu", "GLY" => "Gly",
			   "HIS" => "His", "ILE" => "Ile", "LEU" => "Leu", "LYS" => "Lys",
			   "MET" => "Met", "PHE" => "Phe", "PRO" => "Pro", "SER" => "Ser",
			   "THR" => "Thr", "TRP" => "Trp", "TYR" => "Tyr", "VAL" => "Val" ); 

my %degeneracy_shift_conversion = 
  ( "ala" => { "H" => "H", "HA" => "HA", "HB" => "HB", "C" => "C", "CA" => "CA", "CB" => "CB", "N" => "N" },
    "arg" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HD" => "HD", "HD2" => "HD", "HD3" => "HD", "HE" => "HE", "HH" => "HH", "HH11" => "HH",
	       "HH12" => "HH", "HH21" => "HH", "HH22" => "HH", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG",
	       "CD" => "CD", "CZ" => "CZ", "N" => "N", "NE" => "NE", "NH" => "NH", "NH1" => "NH", "NH2" => "NH" },
    "asn" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD2" => "HD2", "HD21" => "HD2",
	       "HD22" => "HD2", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG", "N" => "N", "ND2" => "ND2" },
    "asp" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "C" => "C", "CA" => "CA",
	       "CB" => "CB", "CG" => "CG", "N" => "N" },
    "cys" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "C" => "C",
	       "CA" => "CA", "CB" => "CB", "N" => "N" },
    "gln" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HE2" => "HE2", "HE21" => "HE2", "HE22" => "HE2", "C" => "C", "CA" => "CA", "CB" => "CB",
	       "CG" => "CG", "CD" => "CD", "N" => "N", "NE2" => "NE2" },
    "glu" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD", "N" => "N" },
    "gly" => { "H" => "H", "HA" => "HA", "HA2" => "HA", "HA3" => "HA", "C" => "C", "CA" => "CA", "N" => "N" },
    "his" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD1" => "HD1", "HD2" => "HD2",
	       "HE1" => "HE1", "HE2" => "HE2", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD2" => "CD2",
	       "CE1" => "CE1", "N" => "N", "ND1" => "ND1", "NE2" => "NE2" },
    "ile" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HG1" => "HG", "HG12" => "HG", "HG13" => "HG", 
	       "HG2" => "HG", "HD"=>"HD", "HD1" => "HD", "C" => "C", "CA" => "CA", "CB" => "CB", "CG1" => "CG", "CG2" => "CG",
	       "CD1" => "CD", "CD"=>"CD", "N" => "N" },
    "leu" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HD" => "HD",
	       "HD1" => "HD", "HD2" => "HD", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD",
	       "CD1" => "CD", "CD2" => "CD", "N" => "N" },
    "lys" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HD" => "HD", "HD2" => "HD", "HD3" => "HD", "HE" => "HE", "HE2" => "HE", "HE3" => "HE",
	       "HZ" => "HZ", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD", "CE" => "CE",
	       "N" => "N", "NZ" => "NZ" },
    "met" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HE" => "HE", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CE" => "CE",
	       "N" => "N" },
    "phe" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD" => "HD", "HD1" => "HD",
	       "HD2" => "HD", "HE1" => "HE", "HE2" => "HE", "HZ" => "HZ", "C" => "C", "CA" => "CA", "CB" => "CB",
	       "CG" => "CG", "CD" => "CD", "CD1" => "CD", "CD2" => "CD", "CE1" => "CE", "CE2" => "CE", "CZ" => "CZ",
	       "N" => "N" },
    "pro" => { "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG", "HG3" => "HG",
	       "HD" => "HD", "HD2" => "HD", "HD3" => "HD", "C" => "C", "CA" => "CA", "CB" => "CB", "CG" => "CG",
	       "CD" => "CD", "N" => "N" },
    "ser" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "C" => "C",
	       "CA" => "CA", "CB" => "CB", "N" => "N" },
    "thr" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HG1" => "HG1", "HG2" => "HG2", "C" => "C", "CA" => "CA",
	       "CB" => "CB", "CG2" => "CG2", "N" => "N" },
    "trp" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD1" => "HD1", "HE1" => "HE1",
	       "HE3" => "HE3", "HZ2" => "HZ2", "HZ3" => "HZ3", "HH2" => "HH2", "C" => "C", "CA" => "CA", "CB" => "CB",
	       "CG" => "CG", "CD1" => "CD1", "CD2" => "CD2", "CE2" => "CE2", "CE3" => "CE3", "CZ2" => "CZ2",
	       "CZ3" => "CZ3", "CH2" => "CH2", "N" => "N", "NE1" => "NE1" },
    "tyr" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD" => "HD", "HD1" => "HD",
	       "HD2" => "HD", "HE" => "HE", "HE1" => "HE", "HE2" => "HE", "HH" => "HH", "C" => "C", "CA" => "CA",
	       "CB" => "CB", "CG" => "CG", "CD" => "CD", "CD1" => "CD", "CD2" => "CD", "CE" => "CE", "CE1" => "CE",
	       "CE2" => "CE", "CZ" => "CZ", "N" => "N" },
    "val" => { "H" => "H", "HA" => "HA", "HB" => "HB", "HG" => "HG", "HG1" => "HG", "HG2" => "HG", "C" => "C",
	       "CA" => "CA", "CB" => "CB", "CG" => "CG", "CG1" => "CG", "CG2" => "CG", "N" => "N" } );

my %cmap_J_conversion = 
    (
     "3JHNHA"        => "J3HNHA"
     );

my %cmap_sse_conversion = 
    (
     "loop"        => "-",
     "alpha-helix" => "h",
     "turn"        => "t",
     "beta-sheet"  => "s"
     );

my %cmap_shift_conversion = 
  ( "ala" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "C" => "CO", "CA" => "CA", "CB" => "CB", "N" => "N15", 
	       "HN" => "HN", "CO" => "CO", "N15" => "N15" },
    "arg" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HD" => "HD", "HD2" => "HD", "HD3" => "HD", "HE" => "HE", "HH" => "HH", "HH1" => "HH", 
	       "HH11" => "HH", "HH2" => "HH", "HH12" => "HH", "HH21" => "HH", "HH22" => "HH", "C" => "CO", 
	       "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD", "CZ" => "CZ", "N" => "N15", "NE" => "NE", 
	       "NH" => "NH", "NH1" => "NH", "NH2" => "NH", "HN" => "HN", "CO" => "CO", "N15" => "N15" },
    "asn" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD2" => "HD", "HD21" => "HD",
	       "HD22" => "HD", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG", "N" => "N15", "ND2" => "ND", 
	       "HN" => "HN", "CO" => "CO", "N15" => "N15", "ND" => "ND", "HD" => "HD" },
    "asp" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "C" => "CO", "CA" => "CA",
	       "CB" => "CB", "CG" => "CG", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15", "OD1" => "OD",
	        "OD2" => "OD", "HD2" => "HD", "OD" => "OD", "HD" => "HD"},
    "cys" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "C" => "CO",
	       "CA" => "CA", "CB" => "CB", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15", "SG" => "SG" },
    "gln" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HE2" => "HE", "HE21" => "HE", "HE22" => "HE", "C" => "CO", "CA" => "CA", "CB" => "CB",
	       "CG" => "CG", "CD" => "CD", "N" => "N15", "NE2" => "NE", "HN" => "HN", "CO" => "CO", "N15" => "N15",
	       "NE" => "NE", "HE" => "HE", "OE1" => "OE", "OE" => "OE"},
    "glu" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD", "N" => "N15", 
	       "HN" => "HN", "CO" => "CO", "N15" => "N15", "OE1" => "OE", "OE2" => "OE", "OE" => "OE", "HE2" => "HE",
	       "HE" => "HE"},
    "gly" => { "H" => "HN", "HA" => "HA", "HA2" => "HA", "HA3" => "HA", "C" => "CO", "CA" => "CA", "N" => "N15", 
	       "HN" => "HN", "CO" => "CO", "N15" => "N15" },
    "his" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD1" => "HD", "HD2" => "HD",
	       "HE1" => "HE", "HE2" => "HE", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD2" => "CD",
	       "CE1" => "CE", "N" => "N15", "ND1" => "ND", "NE2" => "NE", "ND" => "ND", "NE" => "NE", "CD" => "CD", 
	       "HN" => "HN", "CO" => "CO", "N15" => "N15", "CE" => "CE", "HD" => "HD", "HE" => "HE" },
    "ile" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HG1" => "HG", "HG12" => "HG", "HG13" => "HG", 
	       "HG2" => "HG", "HD1" => "HD", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG1" => "CG", "CG2" => "CG",
	       "CD1" => "CD", "N" => "N15", "HD" => "HD", "HN" => "HN", "CO" => "CO", "N15" => "N15", "HG" => "HG",
	       "CD" => "CD", "CG" => "CG" },
    "leu" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HD" => "HD",
	       "HD1" => "HD", "HD2" => "HD", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD",
	       "CD1" => "CD", "CD2" => "CD", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15"},
    "lys" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HD" => "HD", "HD2" => "HD", "HD3" => "HD", "HE" => "HE", "HE2" => "HE", "HE3" => "HE",
	       "HZ" => "HZ", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CD" => "CD", "CE" => "CE",
	       "N" => "N15", "NZ" => "NZ", "HN" => "HN", "CO" => "CO", "N15" => "N15" },
    "met" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG",
	       "HG3" => "HG", "HE" => "HE", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG", "CE" => "CE",
	       "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15" , "SD" => "SD"},
    "phe" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD" => "HD", "HD1" => "HD",
	       "HD2" => "HD", "HE" => "HE", "HE1" => "HE", "HE2" => "HE", "HZ" => "HZ", "C" => "CO", "CA" => "CA", "CB" => "CB",
	       "CG" => "CG", "CD" => "CD", "CD1" => "CD", "CD2" => "CD", "CE1" => "CE", "CE2" => "CE", "CZ" => "CZ",
	       "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15" },
    "pro" => { "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "HG2" => "HG", "HG3" => "HG",
	       "HD" => "HD", "HD2" => "HD", "HD3" => "HD", "C" => "CO", "CA" => "CA", "CB" => "CB", "CG" => "CG",
	       "CD" => "CD", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15" },
    "ser" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HG" => "HG", "C" => "CO",
	       "CA" => "CA", "CB" => "CB", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15", "OG" => "OG" },
    "thr" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HG1" => "HG", "HG2" => "HG", "C" => "CO", "CA" => "CA",
	       "CB" => "CB", "CG2" => "CG", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15", "CG" => "CG",
	       "HG" => "HG" },
    "trp" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD1" => "HD", "HE1" => "HE",
	       "HE3" => "HE", "HZ2" => "HZ", "HZ3" => "HZ", "HH2" => "HH", "C" => "CO", "CA" => "CA", "CB" => "CB",
	       "CG" => "CG", "CD1" => "CD", "CD2" => "CD", "CE2" => "CE", "CE3" => "CE", "CZ2" => "CZ",
	       "CZ3" => "CZ", "CH2" => "CH", "N" => "N15", "NE1" => "NE", "CZ" => "CZ", "HZ" => "HZ", "CD" => "CD",
	       "HN" => "HN", "CO" => "CO", "N15" => "N15", "HE" => "HE", "HH" => "HH", "NE" => "NE", "CE" => "CE",
	       "HD" => "HD", "CH" => "CH" },
    "tyr" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HB2" => "HB", "HB3" => "HB", "HD" => "HD", "HD1" => "HD",
	       "HD2" => "HD", "HE" => "HE", "HE1" => "HE", "HE2" => "HE", "HH" => "HH", "C" => "CO", "CA" => "CA",
	       "CB" => "CB", "CG" => "CG", "CD" => "CD", "CD1" => "CD", "CD2" => "CD", "CE" => "CE", "CE1" => "CE",
	       "CE2" => "CE", "CZ" => "CZ", "N" => "N15", "HN" => "HN", "CO" => "CO", "N15" => "N15", "OH" => "OH" },
    "val" => { "H" => "HN", "HA" => "HA", "HB" => "HB", "HG" => "HG", "HG1" => "HG", "HG2" => "HG", "C" => "CO",
	       "CA" => "CA", "CB" => "CB", "CG" => "CG", "CG1" => "CG", "CG2" => "CG", "N" => "N15", 
	       "HN" => "HN", "CO" => "CO", "N15" => "N15" } );
  
my @CMAP_shift_name_order = ("HN", "N15", "CO", "CA", "CB", "CG", "CD", "CE", "CZ", "CH", "HA", "HB", 
			"HG", "HD", "HE", "HZ", "HH", 
			"CO-1", "CA-1", "CB-1", "CG-1", "CD-1", "CE-1", "CZ-1", "CH-1", "HA-1", "HB-1", "HG-1", "HD-1", "HE-1", "HZ-1", "HH-1");

my %BMRB_shift_name_ordering = ( "H" => 1, "HA" => 2, "HA2" => 3, "HA3" => 4, "HB" => 5, "HB2" => 6, "HB3" => 7, "HG" => 8,
				 "HG1" => 9, "HG12" => 10, "HG13" => 11, "HG2" => 12, "HG3" => 13, "HD1" => 14, "HD2" => 15,
				 "HD21" => 16, "HD22" => 17, "HD3" => 18, "HE" => 19, "HE1" => 20, "HE2" => 21, "HE21" => 22,
				 "HE22" => 23, "HE3" => 24, "HZ" => 25, "HZ2" => 26, "HZ3" => 27, "HH" => 28, "HH11" => 29,
				 "HH12" => 30, "HH2" => 31, "HH21" => 32, "HH22" => 33, "C" => 34, "CA" => 35, "CB" => 36,
				 "CG" => 37, "CG1" => 38, "CG2" => 39, "CD" => 40, "CD1" => 41, "CD2" => 42, "CE" => 43,
				 "CE1" => 44, "CE2" => 45, "CE3" => 46, "CZ" => 47, "CZ2" => 48, "CZ3" => 49, "CH2" => 50,
				 "N" => 51, "ND1" => 52, "ND2" => 53, "NE" => 54, "NE1" => 55, "NE2" => 56, "NZ" => 57,
				 "NH1" => 58, "NH2" => 59 );

;


 my $coupling_constants_saveframe =
 {
  "id" => {
      "bmrbFieldName" => "_Coupling_constant.ID",
      "required" => 1
      },
  "type" => {
      "bmrbFieldName" => "_Coupling_constant.Coupling_constant_code",
      "required" => 1
      },
  "Atom1residueNumber" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_1_chem_comp_seq_num",
      "required" => 1
  },
  "Atom1residueName" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_1_chem_comp_index_ID",
      "required" => 1
  },
  "Atom1atomName" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_1_atom_ID",
      "required" => 1
  },
  "Atom1atomType" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_1_atom_type",
      "required" => 0
  },
  "Atom1ambiguity" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_1_ambiguity_code",
      "required" => 0
  },
  "Atom2residueNumber" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_2_chem_comp_seq_num",
      "required" => 1
  },
  "Atom2residueName" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_2_chem_comp_index_ID",
      "required" => 1
  },
  "Atom2atomName" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_2_atom_ID",
      "required" => 1
  },
  "Atom2atomType" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_2_atom_type",
      "required" => 0
  },
  "Atom2ambiguity" => {
      "bmrbFieldName" => "_Coupling_constant.Atom_2_ambiguity_code",
      "required" => 0
  },
  "value" => {
      "bmrbFieldName" => "_Coupling_constant.Val",
      "required" => 1
  },
  "minValue" => {
      "bmrbFieldName" => "_Coupling_constant.Min_val",
      "required" => 0
  },
  "maxValue" => {
      "bmrbFieldName" => "_Coupling_constant.Max_val",
      "required" => 0
  },
  "error" => {
      "bmrbFieldName" => "_Coupling_constant.Val_err",
      "required" => 1
  }
  };

 my $secondary_structure_saveframe = 
 {
     "id" => {
	 "bmrbFieldName" => "_Secondary_struct.ID",
	 "required" => 1
	 },
     "startResidueNumber" => {
	 "bmrbFieldName" => "_Secondary_struct.Beginning_chem_comp_seq_num",
	 "required" => 1
	 },
     "startResidueName" => {
	 "bmrbFieldName" => "_Secondary_struct.Beginning_chem_comp_index_ID",
	 "required" => 1
	 },
     "endResidueNumber" => {
	 "bmrbFieldName" => "_Secondary_struct.Last_chem_comp_seq_num",
	 "required" => 1
	 },
     "endResidueName" => {
	 "bmrbFieldName" => "_Secondary_struct.Last_chem_comp_index_ID",
	 "required" => 1
	 },
     "sseName" => {
	 "bmrbFieldName" => "_Secondary_struct.Secondary_struct_name",
	 "required" => 0
	 },
     "sseCode" => {
	 "bmrbFieldName" => "_Secondary_struct.Secondary_struct_code",
	 "required" => 1
	 },
     "method" => {
	 "bmrbFieldName" => "_Secondary_struct.Selection_method_ID",
	 "required" => 0
	 }
 };




# clone
#   clones a bmrb_hlist.
#
#        $original_hlist - the original $bmrb_hlist
#        $cloned_hlist   - the cloned $bmrb_hlist
sub clone
  {
  my $original_hlist = shift @_;
  my $cloned_hlist = shift @_;

  # generic cloning code
  my @clone_stack;
  push @clone_stack, { "type" => "HASH", "clone" => $cloned_hlist, "range" => [ keys %$original_hlist ], "index" => 0, "original" => $original_hlist }; 

  while(@clone_stack)
    {
    my $index = $clone_stack[$#clone_stack]{"index"};

    if ($index < @{$clone_stack[$#clone_stack]{"range"}}) # clone a part of the current hash or array
      {
      # get new value
      my $test_value;
      if ($clone_stack[$#clone_stack]{"type"} eq "HASH")
	{ $test_value = $clone_stack[$#clone_stack]{"original"}{$clone_stack[$#clone_stack]{"range"}[$index]}; }
      elsif ($clone_stack[$#clone_stack]{"type"} eq "ARRAY")
	{ $test_value = $clone_stack[$#clone_stack]{"original"}[$index]; }

      my $test_ref = ref($test_value);
      if ($test_ref eq "HASH") # test value is a hash
	{ push @clone_stack, {"type" => "HASH", "clone" => {}, "range" => [ keys %$test_value ], "index" => 0, "original" => $test_value }; }
      elsif ($test_ref eq "ARRAY") # test value is an array
	{ push @clone_stack, {"type" => "ARRAY", "clone" => [], "range" => $test_value, "index" => 0, "original" => $test_value }; }
      elsif ($clone_stack[$#clone_stack]{"type"} eq "HASH") # clone section is a hash
	{
	$clone_stack[$#clone_stack]{"clone"}{$clone_stack[$#clone_stack]{"range"}[$index]} = $test_value;
	$clone_stack[$#clone_stack]{"index"}++;
	}
      else # clone section is an array
	{
	$clone_stack[$#clone_stack]{"clone"}[$index] = $test_value;
	$clone_stack[$#clone_stack]{"index"}++;
	}
      }
    else # pop finished cloned section.
      {
      if (@clone_stack > 1)
	{
	my $index = $clone_stack[$#clone_stack - 1]{"index"};

	if($clone_stack[$#clone_stack - 1]{"type"} eq "HASH") # handle hashes
	  { $clone_stack[$#clone_stack - 1]{"clone"}{$clone_stack[$#clone_stack - 1]{"range"}[$index]} = $clone_stack[$#clone_stack]{"clone"}; }
	else # handle arrays
	  { $clone_stack[$#clone_stack - 1]{"clone"}[$index] = $clone_stack[$#clone_stack]{"clone"}; }
	}
      
      pop @clone_stack;
      if (@clone_stack)
	{ $clone_stack[$#clone_stack]{"index"}++; }
      }
    }
  

  return;
  }


# read_bmrb_file
#   reads a bmrb file and returns a hash of records (Residue is a hash).
#
#       $$bmrb_hlist{"filename"} - name of bmrb file used to build hash of residue names.
#       $$bmrb_hlist{"name_array"} - list of residue names in their proper order.
#	$$bmrb_hlist{"reference"}{"atom_type"} - hash list of references (array of fields) by atom type.
#	$$bmrb_hlist{"reference_fields"} - list and order of reference fields.
#	$$bmrb_hlist{"reference_shift_index"} - index of chemical shift value in reference fields array.
#	$$bmrb_hlist{"sequence"} - string of the amino acid sequence (single letter code).
#	$$bmrb_hlist{"residue_count"} - the number of residues in the polymer.
#	$$bmrb_hlist{"sequence_startpos"} - starting author_index (or index if no author_index).
#       $$bmrb_hlist{"ambiguity_lists"} - array of ambiguity lists (optional).
#	$$bmrb_hlist{"molecule_type"} - molecule_type; polymer.
#	$$bmrb_hlist{"polymer_class"} - polymer_class; DNA, RNA, protein, etc.
#	$$bmrb_hlist{"common_name"} - common name for the molecule (optional).
#	$$bmrb_hlist{"skip_processing"} - flag indicating that processing of usable save frames was skipped (optional).
#
#       $$bmrb_hlist{"save_frames"} - list of save_frames.
#		$$save_frame{"name"} - name of the save_frame.
#		$$save_frame{"type"} - type of save_frame.
#		$$save_frame{"text"} - list of text lines of the save_frame.
#		$$save_frame{"sections"} - (optional) orderred list of sections of the save_frame.
#			$$section{"type"} - type of section.
#			$$section{"value"} - value of the section, if it has a value.
#			$$section{"extra_tags"} - (optional) lists of tags of extra data.
#			$$section{"all_tags"} - (optional) lists of tags of data.
#		$$save_frame{"data"} - organized data structure of save frame data (only for certain save frame types).
#		$$save_frame{"data"} (secondary structure) - array of a secondary structure save frame.
#                       $$ss_elem{"id"} - secondary structure id
#                       $$ss_elem{"startResidueNumber"} - beginning residue number of SSE
#                       $$ss_elem{"startResidueName"} - beginning residue name of SSE
#                       $$ss_elem{"endResidueNumber"} - ending residue number of SSE
#                       $$ss_elem{"endResidueName"} - ending residue name of SSE
#                       $$ss_elem{"sseName"} - SSE name
#                       $$ss_elem{"sseCode"} - SSE codes (loop, alpha-helix, beta-sheet)
#                       $$ss_elem{"method"} - method of secondary structure determination
#		$$save_frame{"data"} (coupling constants) - array of a coupling constant save frame.
#                       $$cc_elem{"id"} - coupling constants id
#                       $$cc_elem{"type"} - description of type of coupling constants
#                       $$cc_elem{"Atom1residueNumber"} - residue number for Atom1
#                       $$cc_elem{"Atom1residueName"} - residue name for Atom1
#                       $$cc_elem{"Atom1atomName"} - atom name for Atom1
#                       $$cc_elem{"Atom1atomType"} - atom type for Atom1
#                       $$cc_elem{"Atom1ambiguity"} - atom ambiguity for Atom1
#                       $$cc_elem{"Atom2residueNumber"} - residue number for Atom2
#                       $$cc_elem{"Atom2residueName"} - residue name for Atom2
#                       $$cc_elem{"Atom2atomName"} - atom name for Atom2
#                       $$cc_elem{"Atom2atomType"} - atom type for Atom2
#                       $$cc_elem{"Atom2ambiguity"} - atom ambiguity for Atom2
#                       $$cc_elem{"value"} - coupling constant value
#                       $$cc_elem{"minValue"} - coupling constant minimum value
#                       $$cc_elem{"maxValue"} - coupling constant maximum value
#                       $$cc_elem{"error"} - coupling constant measurement error (uncertainty)
#		$$save_frame{"data"} (spectral peak list) - organized hash of a spectral peak list save frame.
#			$$peak_list{"entry_id"} - entry id.
#			$$peak_list{"peak_list_id"} - spectral peak list id.
#			$$peak_list{"index_array"} - array of peak indexes.
#			$$peak_list{"details"} - array of lines  describing details for the peak list.
#			$$peak_list{"num_dimensions"} - number of dimensions. 
#			$$peak_list{"experiment_label"} - experiment_label.
#			$$peak_list{"dimension_descriptions"} - array of dimension descriptions.
#				$$dim_description{"dim_id"} - dimension id.
#				$$dim_description{"atom_type"} - atom type.
#				$$dim_description{"spectral_region"} - spectral region.
#				$$dim_description{"magnetization_linkage"} - magnetization linkage.
#				$$dim_description{"sweep_width"} - sweep width.
#				$$dim_description{"encoding"} - encoding code for composed dimensions.
#				$$dim_description{"encoding_dimension"} - source dimension for encoding.
#				$$dim_description{"extra_tags"} - extra tags.
#			$$peak_list{"plist"} - hash of peak index to peak.
#				$$peak{"index"} - index for the peak.
#				$$peak{"dimensions"} - hash of dimension id to dimension.
#					$$dimension{"dim_id"} - dimension id.
#					$$dimension{"shift"} - shift value in this dimension.
#					$$dimension{"shift_error"} - shift standard error in this dimension.
#					$$dimension{"width"} - peak width in this dimension.
#					$$dimension{"width_error"} - peak width standard error in this dimension.
#					$$dimension{"phase"} - peak phase.
#					$$dimension{"phase_error"} - peak phase standard error.
#					$$dimension{"decay"} - peak decay rate.
#					$$dimension{"decay_error"} - peak decay rate standard error.
#					$$dimension{"derivation_method"} - derivation method.
#					$$dimension{"assignments"} - array of shift assignments.
#						$$assignment{"aa"} - amino acid.
#						$$assignment{"index"} - residue index. 
#						$$assignment{"atom_name"} - atom name.
#						$$assignment{"set_id"} - set id for handling ambiguous constraints.
#						$$assignment{"magnetization_linkage"} - magnetization_linkage.  
#						$$assignment{"figure_of_merit"} - figure of merit.  
#						$$assignment{"list_id"} - peak list id. 
#						$$assignment{"list_label"} - peak list label. 
#						$$assignment{"shift_id"} - assigned shift id.
#						$$assignment{"extra_tags"} - extra tags.
#					$$dimension{"extra_tags"} - extra_tags.
#				$$peak{"intensity"} - peak intensity.
#				$$peak{"intensity_error"} - peak intensity standard error.
#				$$peak{"figure_of_merit"} - figure of merit.
#				$$peak{"measurement_method"} - measurement method.
#				$$peak{"derivation_set"} - derivation set.
#				$$peak{"extra_tags"} - extra_tags.
#		
#       $$bmrb_hlist{"rlist"} - hash list of residue_names to residues.
#		$$residue{"name"} - name of residue (uses index in name)
#		$$residue{"author_name"} - name of residue (uses author_index in name)
#		$$residue{"aa"} - amino acid of residue
#		$$residue{"author_index"} - author designated residue number (optional).
#		$$residue{"insertion_code"} - insertion code for aligning with a native sequence (optional).
#		$$residue{"segment_definition_code"} - segment code for identifying important group of residues (optional).
#		$$residue{"index"} - residue number
#		$$residue{"shifts"} - hash of shifts
#			$$shift{"atom_type"} - atom_type of shift.
#			$$shift{"list"} - array of shift values.
#			$$shift{"idlist"} - array of shift ID's.
#			$$shift{"ambiguity_code"} - ambiguity code for the shift_name (optional).
#			$$shift{"error"} - amount of error for the shift value (optional).
#			$$shift{"extra_tags"} - hash of extra tags for the shift (optional).
#		$$residue{"next"} - name of the next residue.
#		$$residue{"prev"} - name of the previous residue.
#		$$residue{"extra_tags"} - hash of extra tags for the residue (optional).
#
#   Parameters:
#       $filename - bmrb file to read.
#	$read_options - reference to hash of read options (optional).
#		$$read_options{"convert_aa_name"} - convert aa names from single letter to three letter. 
#		$$read_options{"convert_shift_names"} - convert shift names to degenerate naming scheme.
#		$$read_options{"shift_conversion_hash"} - ref to hash of shift name conversions.
#		$$read_options{"bmrb_hlist"} - reference to bmrb_hlist to fill.
sub read_bmrb_file
  {
  my $filename = shift @_;
  my $read_options = {};
  if (@_)
    { $read_options = shift @_; }

  # setting read_options
  my $convert_aa_names = 1; 
  if (exists $$read_options{"convert_aa_names"} && ! $$read_options{"convert_aa_names"})
    { $convert_aa_names = 0; }

  my $convert_shift_names = 0; 
  if (exists $$read_options{"convert_shift_names"} && $$read_options{"convert_shift_names"})
    { $convert_shift_names = 1; }
  
  my $shift_conversion_hash = \%degeneracy_shift_conversion;
  if (exists $$read_options{"shift_conversion_hash"} && ref $$read_options{"shift_conversion_hash"} eq "HASH")
    { $shift_conversion_hash = $$read_options{"shift_conversion_hash"}; }

  my $skip_processing = 0;
  if (exists $$read_options{"skip_processing"} && $$read_options{"skip_processing"})
    { $skip_processing = 1; }

  my $limit_save_frames = 0;
  if (exists $$read_options{"limit_save_frames"} && ref $$read_options{"limit_save_frames"} eq "ARRAY")
    { $skip_processing = $$read_options{"limit_save_frames"}; }

  my $delete_save_frames = 0;
  if (exists $$read_options{"delete_save_frames"} && $$read_options{"delete_save_frames"})
    { $delete_save_frames = 1; }

  my $bmrb_hlist;
  if (exists $$read_options{"bmrb_hlist"} && ref $$read_options{"bmrb_hlist"} eq "HASH")
    { $bmrb_hlist = $$read_options{"bmrb_hlist"}; }
  else
    { 
    $bmrb_hlist = {}; 
    $$bmrb_hlist{"filename"} = $filename;
    }

  local *STARFILE;
  if ($filename eq "-")
    { *STARFILE = *STDIN; }
  else
    { open (STARFILE, "<$filename") || die "unable to open $filename"; }


  # divide STARFILE into save_frames.
  my $save_frames_alist = [];
  while (my $line = <STARFILE>)
    {
    # begin reading a save_frame
    if ($line =~ /^\s*save_/)
      {
      my ($save_frame_name) = ($line =~ /^\s*save_([a-zA-Z0-9_-]*)/);
      # skip comment and blank lines
      while (($line = <STARFILE>) && (($line =~ /^\s*$/) ||($line =~ /^\s*\#/))) {}

      my ($save_frame_type) = ($line =~ /_[Ss]aveframe_[Cc]ategory\s+(\w+)/);
      if ($save_frame_type eq "")
	{ ($save_frame_type) = ($line =~ /_[Ss]aveframe_[Cc]ategory\s+(\.)/); }
      if ($save_frame_type eq "")
	{
	chomp $line;
	print STDERR "ERROR LINE: \"",$line,"\"\n";
	die "Null _Saveframe_category";
	}

      if (! $limit_save_frames || (grep {$_ eq $save_frame_type; } (@$limit_save_frames)))
	{
	my $save_frame = { "type" => $save_frame_type, "name" => $save_frame_name, "text" => [] };

	while (my $line = <STARFILE>)
	  {
	  chomp $line;
	  if ($line =~ /^\s*save_/)
	    { last; }
	  
	  push @{$$save_frame{"text"}}, $line; 
	  }      
 
	push @$save_frames_alist, $save_frame; 
	}
      else
	{
	while (my $line = <STARFILE>)
	  {
	  if ($line =~ /^\s*save_/)
	    { last; }	  
	  }      
	}
      }
    }
  
  close STARFILE if ($filename ne "-");


  # create "save_frames" if it does not exist
  if (! exists $$bmrb_hlist{"save_frames"})
    { $$bmrb_hlist{"save_frames"} = []; }

  # add newly read save_frames
  if (! $delete_save_frames)
    { push @{$$bmrb_hlist{"save_frames"}}, @$save_frames_alist; }

  # skip processing usable save_frames if indicated in read_options
  if ($skip_processing)
    {
    $$bmrb_hlist{"skip_processing"} = 1;
    return $bmrb_hlist; 
    }

  # find and process usable save_frames
  foreach my $save_frame (@$save_frames_alist)
    {
    if ($$save_frame{"type"} eq "assigned_chemical_shifts")
      { &process_assigned_chemical_shifts_frame($save_frame, $convert_aa_names, $convert_shift_names, $shift_conversion_hash, $bmrb_hlist); }
    elsif($$save_frame{"type"} eq "monomeric_polymer")
      { &process_monomeric_polymer_frame($save_frame, $convert_aa_names, $bmrb_hlist); }
    elsif($$save_frame{"type"} eq "chemical_shift_reference")
      { &process_chemical_shift_reference_frame($save_frame, $bmrb_hlist); }
    elsif($$save_frame{"type"} eq "spectral_peak_list")
      { &process_spectral_peak_list_frame($save_frame); }
    elsif($$save_frame{"type"} eq "coupling_constants")
      { &process_coupling_constants_frame($save_frame, $bmrb_hlist); }
    elsif($$save_frame{"type"} eq "secondary_structure")
      { &process_secondary_structure_frame($save_frame, $bmrb_hlist); }
    }
  
  # update spectral peak lists assignment info
  if ((grep {$$_{"type"} eq "assigned_chemical_shifts"; } (@$save_frames_alist)) && (grep {$$_{"type"} eq "spectral_peak_list"; } (@$save_frames_alist)))
    { &update_spectral_peak_list_frames($bmrb_hlist, [ (grep {$$_{"type"} eq "spectral_peak_list"; } (@$save_frames_alist)) ]); }

  # ADDED BY GSS 10/29/2000 for bmr1133.str, but applies to a lot more ...
  # if at this point, you do not have a name array, then kinda fake one.
  if ( (! ref $$bmrb_hlist{"name_array"}) || (! @{$$bmrb_hlist{"name_array"}}))
    { $$bmrb_hlist{"name_array"} = [ sort { ($a =~ /^[a-zA-Z]+(\-?\d+)$/)[0] <=> ($b =~ /^[A-Za-z]+(\-?\d+)$/)[0]; } (keys %{$$bmrb_hlist{"rlist"}}) ]; }

  # create sequence field
  if ((! exists $$bmrb_hlist{"sequence"}) || ($$bmrb_hlist{"sequence"} eq ""))
    {
    $$bmrb_hlist{"sequence"} = "";
    for(my $x=0; $x < @{$$bmrb_hlist{"name_array"}}; $x++)
      {
      my $resname = $$bmrb_hlist{"name_array"}[$x];
      
      my $aa = $$bmrb_hlist{"rlist"}{$resname}{"aa"};
      if (exists $aa_name_conversion{lc($aa)})
	{ $aa = $aa_name_conversion{lc($aa)}; }    
      
      $$bmrb_hlist{"sequence"} .= $aa;
      }
    }

  # create residue_count
  if (! exists $$bmrb_hlist{"residue_count"})
    { $$bmrb_hlist{"residue_count"} = scalar(@{$$bmrb_hlist{"name_array"}}); }

  # create prev & next links
  for(my $x=0; $x < @{$$bmrb_hlist{"name_array"}}; $x++)
    {
    my $resname = $$bmrb_hlist{"name_array"}[$x];
    if ($x)
      { $$bmrb_hlist{"rlist"}{$resname}{"prev"} = $$bmrb_hlist{"name_array"}[$x-1]; }
    if ($x < (@{$$bmrb_hlist{"name_array"}} - 1))
      { $$bmrb_hlist{"rlist"}{$resname}{"next"} = $$bmrb_hlist{"name_array"}[$x+1]; }
    }

  # create sequence_startpos field
  if (exists $$bmrb_hlist{"rlist"}{$$bmrb_hlist{"name_array"}[0]}{"author_index"})
    { $$bmrb_hlist{"sequence_startpos"} = $$bmrb_hlist{"rlist"}{$$bmrb_hlist{"name_array"}[0]}{"author_index"}; }
  else
    { $$bmrb_hlist{"sequence_startpos"} = $$bmrb_hlist{"rlist"}{$$bmrb_hlist{"name_array"}[0]}{"index"}; }

  return $bmrb_hlist; 
  }


# write_bmrb_file
#   Prints a bmrb file out to $filename
#
#   Parameters:
#	$bmrb_hlist - reference to bmrb hash structure.
#	$filename - name of output filename.
#       $write_options - hash of options.
sub write_bmrb_file
  {
  my $bmrb_hlist = shift @_;
  my $filename = shift @_; 
  my $write_options = {};
  if (@_)
    { $write_options = shift @_; }
  
  # do nothing if there are no save_frames and "skip_processing" is true
  if ((! exists $$bmrb_hlist{"save_frames"} || ! @{$$bmrb_hlist{"save_frames"}}) && (exists $$bmrb_hlist{"skip_processing"} && $$bmrb_hlist{"skip_processing"}))
    { return; }
  
  # create empty array of save_frames if it does not exist.
  if (! exists $$bmrb_hlist{"save_frames"})
    { $$bmrb_hlist{"save_frames"} = []; }
  

  # update spectral peak lists assignment info
  if ((grep {$$_{"type"} eq "assigned_chemical_shifts"; } (@{$$bmrb_hlist{"save_frames"}})) && (grep {$$_{"type"} eq "spectral_peak_list"; } (@{$$bmrb_hlist{"save_frames"}})))
  { &update_spectral_peak_list_frames($bmrb_hlist, [ (grep {$$_{"type"} eq "spectral_peak_list"; } (@{$$bmrb_hlist{"save_frames"}})) ]); }

    
  # create monomeric_polymer save_frame if it does not exist
  if ( ! (grep {$$_{"type"} eq "monomeric_polymer"; } (@{$$bmrb_hlist{"save_frames"}})) && 
       (exists $$bmrb_hlist{"rlist"} && scalar(%{$$bmrb_hlist{"rlist"}})))
    {
    my $save_frame = { "type" => "monomeric_polymer", "name" => "monomeric_polymer", "sections" => [] };
    if (exists $$bmrb_hlist{"common_name"} && ($$bmrb_hlist{"common_name"} ne ""))
      { 
      $$save_frame{"name"} = $$bmrb_hlist{"common_name"};
      $$save_frame{"name"} =~ tr/ /_/s;
      }

    my $sections = $$save_frame{"sections"};
    
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "molecule_type" };
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "polymer_class" };
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "common_name" };
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "residue_count" };
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "sequence" };
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "sequence_loop", "all_tags" => [ "_Residue_seq_code", "_Residue_label"] };
    if (exists $$write_options{"extra_tags"} && exists $$write_options{"extra_tags"}{"residues"})
      { 
      $$sections[$#$sections]{"extra_tags"} = $$write_options{"extra_tags"}{"residues"}; 
      push @{$$sections[$#$sections]{"all_tags"}}, @{$$write_options{"extra_tags"}{"residues"}};
      }
    if (exists $$write_options{"optional_tags"} && exists $$write_options{"optional_tags"}{"residues"})
      { push @{$$sections[$#$sections]{"all_tags"}}, @{$$write_options{"optional_tags"}{"residues"}}; }

    push @$sections, { "type" => "text", "value" => [ "" ] };
    
    push @{$$bmrb_hlist{"save_frames"}}, $save_frame;
    }
  
  # create chemical_shift_reference save_frame if it does not exist
  if ( ! (grep {$$_{"type"} eq "chemical_shift_reference"; } (@{$$bmrb_hlist{"save_frames"}})) && 
       (exists $$bmrb_hlist{"reference_fields"} && @{$$bmrb_hlist{"reference_fields"}}))
    {
    my $save_frame = { "type" => "chemical_shift_reference", "name" => "chemical_shift_reference", "sections" => [] };
    my $sections = $$save_frame{"sections"};
    
    push @$sections, { "type" => "text", "value" => [ "" ] };
    push @$sections, { "type" => "reference_loop", "all_tags" => [@{$$bmrb_hlist{"reference_fields"}}]  };
    push @$sections, { "type" => "text", "value" => [ "" ] };

    push @{$$bmrb_hlist{"save_frames"}}, $save_frame;
    }
  # create assigned_chemical_shifts save_frame if it does not exist
  if ( ! (grep {$$_{"type"} eq "assigned_chemical_shifts"; } (@{$$bmrb_hlist{"save_frames"}})) && &has_shifts($bmrb_hlist))
    {
	
    my $save_frame = { "type" => "assigned_chemical_shifts", "name" => "assigned_chemical_shifts", "sections" => [] };
    my $sections = $$save_frame{"sections"};
    
    push @$sections, { "type" => "text", "value" => [ "" ] };
    my $chem_section = { "type" => "chemical_shifts_loop" };
    if (exists $$write_options{"extra_tags"} && exists $$write_options{"extra_tags"}{"shifts"})
      { 
      $$chem_section{"extra_tags"} = $$write_options{"extra_tags"}{"shifts"}; 
      $$chem_section{"all_tags"} = [ "_Atom_shift_assign_ID", "_Residue_seq_code", "_Residue_label", "_Atom_name", "_Atom_type", "_Chem_shift_value", "_Chem_shift_value_error", "_Chem_shift_ambiguity_code", @{$$write_options{"extra_tags"}{"shifts"}} ]; 
      }
    else
      { $$chem_section{"all_tags"} = [ "_Atom_shift_assign_ID", "_Residue_seq_code", "_Residue_label", "_Atom_name", "_Atom_type", "_Chem_shift_value", "_Chem_shift_value_error", "_Chem_shift_ambiguity_code" ]; }
    
    push @$sections, $chem_section;
    push @$sections, { "type" => "text", "value" => [ "" ] };
    if (exists $$bmrb_hlist{"ambiguity_lists"} && @{$$bmrb_hlist{"ambiguity_lists"}})
      { 
      push @$sections, { "type" => "ambiguity_loop" }; 
      push @$sections, { "type" => "text", "value" => [ "" ] };
      }
    
    push @{$$bmrb_hlist{"save_frames"}}, $save_frame;
    }
  


  # start writing BMRB file
  local *STARFILE;
  if ($filename eq "-")
    { *STARFILE = *STDOUT; }
  else
    { open (STARFILE, ">$filename") || die "unable to open $filename"; }
  
  # create name_array if it does not exist
  if (! exists $$bmrb_hlist{"name_array"} || ! (ref $$bmrb_hlist{"name_array"} eq "ARRAY"))
    { $$bmrb_hlist{"name_array"} = [ sort { ($a =~ /^[a-zA-Z]+(\-?\d+)$/)[0] <=> ($b =~ /^[A-Za-z]+(\-?\d+)$/)[0]; } (keys %{$$bmrb_hlist{"rlist"}}) ]; }
  
  # create sequence field
  if ((! exists $$bmrb_hlist{"sequence"}) || ($$bmrb_hlist{"sequence"} eq ""))
    {
    $$bmrb_hlist{"sequence"} = "";
    for(my $x=0; $x < @{$$bmrb_hlist{"name_array"}}; $x++)
      {
      my $resname = $$bmrb_hlist{"name_array"}[$x];
      
      my $aa = $$bmrb_hlist{"rlist"}{$resname}{"aa"};
      if (exists $aa_name_conversion{lc($aa)})
	{ $aa = $aa_name_conversion{lc($aa)}; }    
      
      $$bmrb_hlist{"sequence"} .= $aa;
      }
    }
  
  # create residue_count
  if (! exists $$bmrb_hlist{"residue_count"})
    { $$bmrb_hlist{"residue_count"} = scalar(@{$$bmrb_hlist{"name_array"}}); }
  
  # update shift ids across assigned chemical shifts frame and spectral peak list frames
  &update_shift_ids($bmrb_hlist);
  
  # loop through save_frames
  my $save_frame_types = {};
  foreach my $save_frame (@{$$bmrb_hlist{"save_frames"}})
    {
    my $text_lines;

    if (($$save_frame{"type"} eq "assigned_chemical_shifts") && exists $$save_frame{"sections"} && @{$$save_frame{"sections"}} && ! $$save_frame_types{$$save_frame{"type"}})
      { 
      $text_lines = &create_assigned_chemical_shifts_frame($save_frame, $bmrb_hlist, $write_options); 
      $$save_frame_types{$$save_frame{"type"}}++;
      }
    elsif (($$save_frame{"type"} eq "monomeric_polymer") && exists $$save_frame{"sections"} && @{$$save_frame{"sections"}} && ! $$save_frame_types{$$save_frame{"type"}})
      { 
      $text_lines = &create_monomeric_polymer_frame($save_frame, $bmrb_hlist, $write_options); 
      $$save_frame_types{$$save_frame{"type"}}++;
      }
    elsif (($$save_frame{"type"} eq "chemical_shift_reference") && exists $$save_frame{"sections"} && @{$$save_frame{"sections"}} && ! $$save_frame_types{$$save_frame{"type"}})
      { 
      $text_lines = &create_chemical_shift_reference_frame($save_frame, $bmrb_hlist); 
      $$save_frame_types{$$save_frame{"type"}}++;
      }
    elsif (($$save_frame{"type"} eq "spectral_peak_list") && exists $$save_frame{"sections"} && @{$$save_frame{"sections"}})
      { $text_lines = &create_spectral_peak_list_frame($save_frame, $bmrb_hlist); }
    elsif (($$save_frame{"type"} eq "coupling_constants") && exists $$save_frame{"sections"} && @{$$save_frame{"sections"}})
      { $text_lines = &create_coupling_constants_frame($save_frame, $bmrb_hlist); }
    elsif (($$save_frame{"type"} eq "secondary_structure") && exists $$save_frame{"sections"} && @{$$save_frame{"sections"}})
      { $text_lines = &create_secondary_structure_frame($save_frame, $bmrb_hlist); }
    elsif (exists $$save_frame{"text"} && @{$$save_frame{"text"}})
      { $text_lines = $$save_frame{"text"}; }
    else # just in case something is not quite right
      { $text_lines = []; }

    if (@$text_lines)
      {
      print STARFILE "save_";
      if (exists $$save_frame{"name"})
	{ print STARFILE $$save_frame{"name"}; }
      print STARFILE "\n";

      print STARFILE "   _Saveframe_category ";
      if (exists $$save_frame{"type"})
	{ print STARFILE $$save_frame{"type"}; }
      else
	{ print STARFILE "."; }
      print STARFILE "\n";
      
      foreach my $line (@$text_lines)
	{ print STARFILE $line,"\n"; }

      print STARFILE "save_\n\n";
      }
    }
      
  close STARFILE;
  }


# convert_bmrb_shift_names
#   Converts the bmrb shift names using the given shift_conversion_hash.  assumes lowercase 3 letter code for Residue type
#
#   Parameters:
#	$bmrb_hlist - reference to bmrb hash structure.
#	$shift_conversion_hash - reference to a shift conversion hash. 
sub convert_bmrb_shift_names
  {
  my $bmrb_hlist = shift @_;
  my $shift_conversion_hash = shift @_;

  foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
    {
    my $residue = $$bmrb_hlist{"rlist"}{$residue_name};
    my $new_shift_list = {};
    foreach my $shift_name_real (keys %{$$residue{"shifts"}})
    {
      my ($shift_name, $suffix) = ($shift_name_real =~ /^([a-zA-Z]+\d*)(\-1)?$/);
      my $res_name = $$residue{"aa"};
      $res_name = $$bmrb_hlist{"rlist"}{$$bmrb_hlist{"rlist"}{$residue_name}{"prev"}}{"aa"}  if ($suffix ne "");
      if (exists $aa_name_conversion2{uc($res_name)})
	{ $res_name = $aa_name_conversion2{uc($res_name)}; }
      $res_name = lc($res_name);

      if (exists $$shift_conversion_hash{$res_name}{$shift_name})
	{
	if (exists $$new_shift_list{$$shift_conversion_hash{$res_name}{$shift_name} . $suffix})
	  {
	      
	      push @{$$new_shift_list{$$shift_conversion_hash{$res_name}{$shift_name} . $suffix}{"list"}}, @{$$residue{"shifts"}{$shift_name . $suffix}{"list"}};
	  }
	else
	  { $$new_shift_list{$$shift_conversion_hash{$res_name}{$shift_name} . $suffix} = $$residue{"shifts"}{$shift_name . $suffix}; }
	}
      }
    $$residue{"shifts"} = $new_shift_list;
    }

  }

# adjust_by_reference
#   Adjusts the carbon chemical shifts to DSS standard based on their current reference.
#
#   Parameters:
#	$bmrb_hlist - bmrb hash structure.
#
sub adjust_by_reference
  {
  my $bmrb_hlist = shift @_;
  
  my $carbon_shifting = 0.0; 
  if (exists $$bmrb_hlist{"reference"}{"C"})
    {
    if ($$bmrb_hlist{"reference"}{"C"}[0] eq "TMS")
      { $carbon_shifting = 1.7; }
    elsif ($$bmrb_hlist{"reference"}{"C"}[0] eq "TSP")
      { $carbon_shifting = -0.15; }
    elsif ($$bmrb_hlist{"reference"}{"C"}[0] eq "dioxane" && exists $$bmrb_hlist{"reference_shift_index"} &&
	   ($$bmrb_hlist{"reference"}{"C"}[$$bmrb_hlist{"reference_shift_index"}] == 0))
      { $carbon_shifting = 69.3; }
    }

  if ($carbon_shifting != 0.0)
    {
    foreach my $residue (values %{$$bmrb_hlist{"rlist"}})
      {
      foreach my $shift (values %{$$residue{"shifts"}})
	{ 
	if ($$shift{"atom_type"} eq "C")
	  {
	  foreach my $value (@{$$shift{"list"}})
	    { $value += $carbon_shifting; }
	  }
	}
      }
    }

  }

# convert_residue_names
#   Converts residue names in the bmrb file structure from 3-letter to 1-letter and visa versa
#
#   Parameters:
#	$bmrb_hlist - bmrb hash structure.
#	$conversion - 1 or 3 for conversion to 1-letter or 3-letter
#
sub convert_residue_names
  {
  my $bmrb_hlist = shift @_;
  my $conversion = shift @_;

  if ($conversion == 1)
    {
    my $rlist = {};
    foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
      {
      my $residue = $$bmrb_hlist{"rlist"}{$residue_name};
      if (exists $aa_name_conversion{lc($$residue{"aa"})})
	{ 
	$$residue{"aa"} = $aa_name_conversion{lc($$residue{"aa"})};
	$$residue{"name"} = $$residue{"aa"} . $$residue{"index"};
	if (exists $$residue{"author_index"})
	  { $$residue{"author_name"} = $$residue{"aa"} . $$residue{"author_index"}; }
	$residue_name = $$residue{"name"};
	}

      $$rlist{$residue_name} = $residue;
      }

    $$bmrb_hlist{"rlist"} = $rlist;
    }
  else
    {
    my $rlist = {};
    foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
      {
      my $residue = $$bmrb_hlist{"rlist"}{$residue_name};
      if (exists $aa_name_conversion2{uc($$residue{"aa"})})
	{ 
	$$residue{"aa"} = $aa_name_conversion2{uc($$residue{"aa"})};
	$$residue{"name"} = $$residue{"aa"} . $$residue{"index"};
	if (exists $$residue{"author_index"})
	  { $$residue{"author_name"} = $$residue{"aa"} . $$residue{"author_index"}; }
	$residue_name = $$residue{"name"};
	}

      $$rlist{$residue_name} = $residue;
      }

    $$bmrb_hlist{"rlist"} = $rlist;    
    }
  }


# readBMRBasCMAP
#   Reads a bmrb file in a CMap naming scheme and returns a BMRB/CMap hash of records. 
#
#   Parameters:
#       $file - bmrb filename to read
#       $restriction_list - ref to list of atom names to restrict to (optional).

sub readBMRBasCMAP
  {
  my $input_bmrb_filename = shift @_;
  

  my $user_shift_name_list = 0;
  
  if (@_ && @{$_[0]})
    {
    my $shift_list = shift @_;
    $user_shift_name_list = {};
    foreach my $shift_name (@$shift_list)
      { $$user_shift_name_list{$shift_name} = 1; }
    }
  

  # read the bmrb file
  my %read_options = ( "shift_conversion_hash" => \%cmap_shift_conversion, "convert_aa_names" => 1, "convert_shift_names" => 1 );
  my $bmrb_hlist1 = &read_bmrb_file($input_bmrb_filename, \%read_options );
  
  # convert to single letter amino acid names
  &convert_residue_names($bmrb_hlist1,1);

  # update "-1" shifts
  if (ref $user_shift_name_list)
    {
    my @previous_shifts = grep {$_ =~ /-1/; } (keys %$user_shift_name_list);
    if (@previous_shifts)
      {
      
      @previous_shifts = map { my ($shift) = ($_ =~ /^(.+)-1/); $shift; } (@previous_shifts);
      foreach my $residue (values %{$$bmrb_hlist1{"rlist"}})
	{
	foreach my $shift_name (keys %{$$residue{"shifts"}})
	  {
	  if (scalar(grep { $shift_name eq $_ } (@previous_shifts)) && exists $$residue{"next"})
	    {
	    my $prev_residue = $$bmrb_hlist1{"rlist"}{$$residue{"next"}}{"shifts"}{$shift_name . "-1"} = $$residue{"shifts"}{$shift_name};
	    }
	  }
	}
      }
    }
  
  # determine the list of shift names to use
  my %name_test_hash;
  foreach my $residue_name (@{$$bmrb_hlist1{"name_array"}})
    {
    foreach my $shift_name (keys %{$$bmrb_hlist1{"rlist"}{$residue_name}{"shifts"}})
      { $name_test_hash{$shift_name} = 1; }
    }

  $$bmrb_hlist1{"parsedtokens"} = [ grep { (exists $name_test_hash{$_} && (!$user_shift_name_list || exists $$user_shift_name_list{$_})); } @CMAP_shift_name_order ];
#  unshift @{$$bmrb_hlist1{"parsedtokens"}}, "AA";

  return $bmrb_hlist1;
  }


# convertBMRBtoCMAP
#   Converts a bmrb hlist into a CMap naming scheme and returns a BMRB/CMap hash of records. 
#
#   Parameters:
#       $file - bmrb filename to read
#       $restriction_list - ref to list of atom names to restrict to (optional).
sub convertBMRBtoCMap
{
    my $bmrb_hlist1 = shift @_;
    my $user_shift_name_list = 0;
  
    if (@_ && @{$_[0]})
    {
	my $shift_list = shift @_;
	$user_shift_name_list = {};
	foreach my $shift_name (@$shift_list)
	{ $$user_shift_name_list{$shift_name} = 1; }
    }
    
    &convert_bmrb_shift_names($bmrb_hlist1, \%cmap_shift_conversion);
    &convert_residue_names($bmrb_hlist1,1);
    
    # update "-1" shifts
    if (ref $user_shift_name_list)
    {
	my @previous_shifts = grep {$_ =~ /-1/; } (keys %$user_shift_name_list);
	if (@previous_shifts)
	{
	    
	    @previous_shifts = map { my ($shift) = ($_ =~ /^(.+)-1/); $shift; } (@previous_shifts);
	    foreach my $residue (values %{$$bmrb_hlist1{"rlist"}})
	    {
		foreach my $shift_name (keys %{$$residue{"shifts"}})
		{
		    if (scalar(grep { $shift_name eq $_ } (@previous_shifts)) && exists $$residue{"next"})
		    {
			my $prev_residue = $$bmrb_hlist1{"rlist"}{$$residue{"next"}}{"shifts"}{$shift_name . "-1"} = $$residue{"shifts"}{$shift_name};
		    }
		}
	    }
	}
    }
    
    # determine the list of shift names to use
    my %name_test_hash;
    foreach my $residue_name (@{$$bmrb_hlist1{"name_array"}})
    {
	foreach my $shift_name (keys %{$$bmrb_hlist1{"rlist"}{$residue_name}{"shifts"}})
	{ $name_test_hash{$shift_name} = 1; }
    }
    
    $$bmrb_hlist1{"parsedtokens"} = [ grep { (exists $name_test_hash{$_} && (!$user_shift_name_list || exists $$user_shift_name_list{$_})); } @CMAP_shift_name_order ];
#    unshift @{$$bmrb_hlist1{"parsedtokens"}}, "AA";
    
    return $bmrb_hlist1;
    
}


####################################### deMultiplicate ########################################
# Input   : a bmrb_hlist type                                                                 # 
# Output  : a bmrb_hlist type                                                                 #
# Purpose : To return only the proper multiplicity of the bmrb_hlist                          #
###############################################################################################
sub deMultiplicate
  {
  my $bmrb_hlist = shift @_;

  foreach my $residue_name (keys %{$$bmrb_hlist{"rlist"}})
    {
    foreach my $shift_name_real (keys %{$$bmrb_hlist{"rlist"}{$residue_name}{"shifts"}})
      {
      my $suffix = "";
      my $shift_name = ($shift_name_real =~ /^(.*?)(\-1)?$/)[0];
      $suffix = "-1"   if ($shift_name ne $shift_name_real);
      if ( ($suffix ne "") && ( exists $$bmrb_hlist{"rlist"}{$residue_name}{"prev"}) && ( exists $multiplicity{$aa_name_conversion{lc($$bmrb_hlist{"rlist"}{$$bmrb_hlist{"rlist"}{$residue_name}{"prev"}}{"aa"})}}{$shift_name} ) )
	{
	splice(@{$$bmrb_hlist{"rlist"}{$residue_name}{"shifts"}{$shift_name_real}{"list"}}, $multiplicity{$aa_name_conversion{lc($$bmrb_hlist{"rlist"}{$$bmrb_hlist{"rlist"}{$residue_name}{"prev"}}{"aa"})}}{$shift_name});
	}
      elsif ( exists $multiplicity{$aa_name_conversion{lc($$bmrb_hlist{"rlist"}{$residue_name}{"aa"})}}{$shift_name_real} )
	{
	splice(@{$$bmrb_hlist{"rlist"}{$residue_name}{"shifts"}{$shift_name_real}{"list"}}, $multiplicity{$aa_name_conversion{lc($$bmrb_hlist{"rlist"}{$residue_name}{"aa"})}}{$shift_name_real});
	}
      }
    }
  }

################################# convertAtomNameBMRBtoCMap ###################################
# Input   : an amino acid and an BMRB atom name                                               # 
# Output  : a CMap atom name                                                                  #
# Purpose : To convert a BMRB atom name to its equivalent CMap atom name                      #
###############################################################################################
sub convertAtomNameBMRBtoCMap
{
    my $aa        = shift @_;
    my $atom_name = shift @_;

    
#    print lc($aa_name_conversion2{uc($aa)}), ":", $atom_name, "\n";
    return $cmap_shift_conversion{lc($aa_name_conversion2{uc($aa)})}{$atom_name};
}



################################# convertSSelemBMRBtoCMap #####################################
# Input   : a BMRB secondary structure element code                                           # 
# Output  : a CMap secondary structure element code                                           #
# Purpose : To convert a BMRB SSE code to its equivalent CMap SSE code                        #
###############################################################################################
sub convertSSelemBMRBtoCMap
{
    my $bmrb_sse        = shift @_;

    
#    print lc($aa_name_conversion2{uc($aa)}), ":", $atom_name, "\n";
    return $cmap_sse_conversion{lc($bmrb_sse)};
}

#################################### convertJBMRBtoCMap #######################################
# Input   : a BMRB J coupling constant code                                                   # 
# Output  : a CMap J coupling constant code                                                   #
# Purpose : To convert a BMRB J code to its equivalent CMap J code                            #
###############################################################################################

sub convertJBMRBtoCMap
{
    my $bmrb_J        = shift @_;

    
#    print lc($aa_name_conversion2{uc($aa)}), ":", $atom_name, "\n";
    return $cmap_J_conversion{uc($bmrb_J)};
}




#
#
#  Routines for internal use only.
#
#


#
#
#     Routines for reading BMRB files
#
#


# loop_contents
#   checks to see whether the loop_ fields contain the required and returns a hash of field name to field order.
#
#   Parameters:
#	$line_array - reference to array of lines.
#	$start - starting position in array to test
#	$required_fields - array of elements to check against the array of lines
sub loop_contents
  {
  my $line_array = shift @_;
  my $start = shift @_;
  my $required_fields = shift @_;
  
  my $columns = {};
  my $required_count = 0;
  # Check all elements ...
  my $x=0;
  while (($start < @$line_array) && ($$line_array[$start] !~ /^\s*$/))
    {
    # skip comment lines
    if ($$line_array[$start] =~ /^\s*\#/)
      {
      $start++;
      next;
      }

    my ($field_name) = ($$line_array[$start] =~ /^\s*([A-Za-z0-9_.]+)/);
    my @new_name = (grep { $field_name =~ /^$_$/i; } (@$required_fields));
    if (@new_name)
      { 
      $required_count++;
      $field_name = $new_name[0]; 
      }
    
    $$columns{$field_name} = $x;
    $x++;
    $start++;
    }
  
  my @found_fields = (keys %$columns);
  if ($required_count != @$required_fields) # this test could be erroneous; however, it is very unlikely
    { return 0; }
  
  return $columns;
  }


# process_assigned_chemical_shifts_frame
#   Processes the assigned chemical shift lines and puts them in the bmrb hash structure.
#
#   Parameters:
#       $save_frame - save_frame hash
#	$convert_aa_names - converts single letter amino acid names to three letter representations if true.
#       $convert_shift_names - convert shift names to degenerate naming scheme.
#       $shift_conversion_hash - hash to use for converting shift names.
#	$bmrb_hlist - bmrb hash structure to fill.
#
sub process_assigned_chemical_shifts_frame
  {
  my $save_frame = shift @_;
  my $convert_aa_names = shift @_;
  my $convert_shift_names = shift @_;
  my $shift_conversion_hash = shift @_;
  my $bmrb_hlist = shift @_;

  my $sections = [];
  my @expected_chem_shift_fields = ("_Atom_shift_assign_ID", "_Residue_seq_code", "_Residue_label", "_Atom_name", "_Atom_type", "_Chem_shift_value", "_Chem_shift_value_error", "_Chem_shift_ambiguity_code");
  my @required_chem_shift_fields = ("_Atom_shift_assign_ID", "_Residue_seq_code", "_Residue_label", "_Atom_name", "_Atom_type", "_Chem_shift_value");

  my $orig_text = $$save_frame{"text"};
  # foreach line in the file until stop_
  for(my $x=0; $x < @$orig_text; $x++)
    {
    my $columns = 0;

    # process the chemical shifts
    if (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_chem_shift_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      my @extra_tags = (grep {my $test = $_; (! grep {$test eq $_; } (@expected_chem_shift_fields)); } (keys %$columns));
      $x = &process_chemical_shift_lines($orig_text,$x,$columns,$convert_aa_names, $convert_shift_names, $shift_conversion_hash, $bmrb_hlist, \@extra_tags);
      push @$sections, { "type" => "chemical_shifts_loop", "extra_tags" => [@extra_tags], "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    # process the ambiguity list
    elsif(($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,["_Atom_shift_assign_ID_ambiguity"])))
      {
      $x += 1 + scalar(keys %$columns);
      $x = &process_chemical_shift_ambiguity($orig_text,$x,$columns,$bmrb_hlist);
      push @$sections, { "type" => "ambiguity_loop",  "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    else
      {
      if (! @$sections || ($$sections[$#{$sections}]{"type"} ne "text")) 
	{ push @$sections, { "type" => "text", "value" => [] }; }
      push @{$$sections[$#{$sections}]{"value"}}, $$orig_text[$x];
      }
    }

  if (@$sections)
    { 
    $$save_frame{"sections"} = $sections; 
    delete $$save_frame{"text"}; 
    }
  }


# process_chemical_shift_ambiguity
#   Processes the assigned chemical shift ambiguity lines and puts them in the bmrb hash structure.
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$bmrb_hlist - bmrb hash structure to fill.
sub process_chemical_shift_ambiguity
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $bmrb_hlist = shift @_;

  $$bmrb_hlist{"ambiguity_lists"} = [];
  
  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }

    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }

    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    push @{$$bmrb_hlist{"ambiguity_lists"}}, [ split(/,/, $tokens[$$column_elements{"_Atom_shift_assign_ID_ambiguity"}]) ];    
    }
  continue
    { $start++; }

  return $start;
  }


# process_chemical_shift_lines
#   Processes the assigned chemical shift lines and puts them in the bmrb hash structure.
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$convert_aa_names - converts single letter amino acid names to three letter representations if true.
#       $convert_shift_names - convert shift names to degenerate naming scheme.
#       $shift_conversion_hash - hash to use for converting shift names.
#	$bmrb_hlist - bmrb hash structure to fill.
#	$extra_tags - array of extra tags in the column_elements.
sub process_chemical_shift_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $convert_aa_names = shift @_;
  my $convert_shift_names = shift @_;
  my $shift_conversion_hash = shift @_;
  my $bmrb_hlist = shift @_;
  my $extra_tags = shift @_;

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }
    
    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }


    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    # as long as you have enough tokens to map to the column elements and the residue label is legitimate continue, otherwise next line.
    next if ((@tokens < scalar(keys %$column_elements)) || 
              ((! exists $aa_name_conversion{lc($tokens[$$column_elements{"_Residue_label"}])}) 
            && (! exists $aa_name_conversion2{uc($tokens[$$column_elements{"_Residue_label"}])}))
             );
 #   print STDERR "AFTER IF\n";
    
    # if you are supposed to convert the name and you can, then do so ...
    if ($convert_aa_names && exists $aa_name_conversion{lc($tokens[$$column_elements{"_Residue_label"}])})
      { $tokens[$$column_elements{"_Residue_label"}] = $aa_name_conversion{lc($tokens[$$column_elements{"_Residue_label"}])}; }

    # get the residue name, by hook or crook.
      my $residue_name = (exists $$column_elements{"_Residue_seq_code"}) ? $tokens[$$column_elements{"_Residue_label"}] . $tokens[$$column_elements{"_Residue_seq_code"}] : $tokens[$$column_elements{"_Residue_label"}] . $tokens[$$column_elements{"_Residue_author_seq_code"}];

  #q  print STDERR "YO >> ",$residue_name, "\n"; 
    my $residue;

    if (! exists $$bmrb_hlist{"rlist"}{$residue_name})
      {
      # Fill the residue hash reference with useful goodies 
      $residue = {};
      $$residue{"index"} = (exists $$column_elements{"_Residue_seq_code"}) ? $tokens[$$column_elements{"_Residue_seq_code"}] : $tokens[$$column_elements{"_Residue_author_seq_code"}];
      $$residue{"author_index"} = $tokens[$$column_elements{"_Residue_author_seq_code"}]    if (exists $$column_elements{"_Residue_author_seq_code"} && ($tokens[$$column_elements{"_Residue_author_seq_code"}] ne "."));
      $$residue{"aa"} = $tokens[$$column_elements{"_Residue_label"}];
      $$residue{"name"} = $$residue{"aa"} . $$residue{"index"};
      $$residue{"author_name"} = $$residue{"aa"} . $$residue{"author_index"}    if (exists $$residue{"author_index"});
      $$bmrb_hlist{"rlist"}{$residue_name} = $residue;
      }
    # it already exists, so just reset the stuff that you would essentially re-read
    else
      { $residue = $$bmrb_hlist{"rlist"}{$residue_name};}

    # if you are using a degenerate naming scheme then degenerate it ...
    if ($convert_shift_names)
      {
      my ($new_token, $suffix) = ($tokens[$$column_elements{"_Atom_name"}] =~ /^([a-zA-Z]+\d*)(\-1)??$/);

      my $res_name = $$residue{"aa"};
      $res_name = $$bmrb_hlist{"rlist"}{$$bmrb_hlist{"rlist"}{$residue_name}{"prev"}}{"aa"}  if ($suffix ne "");
      if (exists $aa_name_conversion2{uc($res_name)})
	{ $res_name = $aa_name_conversion2{uc($res_name)}; }

#      print STDERR "BF>", $residue_name, " | $res_name | " , $tokens[$$column_elements{"_Atom_name"}], " >> ", "$new_token :: $suffix\n";
      $tokens[$$column_elements{"_Atom_name"}] = $$shift_conversion_hash{lc($res_name)}{$new_token} . $suffix; 
#      print STDERR "AF>", $residue_name, " | $res_name | " , $tokens[$$column_elements{"_Atom_name"}], " >> ", "$new_token :: $suffix\n";
      } 

    # if you do not already have values for the shift and atomtype then setup the atom type.
    if (! exists $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]})
      { 
      $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"atom_type"} = $tokens[$$column_elements{"_Atom_type"}];
      $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"ambiguity_code"} = $tokens[$$column_elements{"_Chem_shift_ambiguity_code"}] if ($tokens[$$column_elements{"_Chem_shift_ambiguity_code"}] ne ".");
      $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"list"} = []; 
      $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"idlist"} = []; 

      $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"error"} = $tokens[$$column_elements{"_Chem_shift_value_error"}] if ($tokens[$$column_elements{"_Chem_shift_value_error"}] ne ".");
      }

    # if the atom name is defined then push the chemical shift value and the extra_tags
    if ($tokens[$$column_elements{"_Atom_name"}] ne "")
       { 
       push @{$$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"list"}}, $tokens[$$column_elements{"_Chem_shift_value"}]; 
       push @{$$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"idlist"}}, $tokens[$$column_elements{"_Atom_shift_assign_ID"}]; 
       
       foreach my $field (@$extra_tags)
	 { $$residue{"shifts"}{$tokens[$$column_elements{"_Atom_name"}]}{"extra_tags"}{$field} = $tokens[$$column_elements{$field}] if ($tokens[$$column_elements{$field}] ne "."); }
       }
    }
  continue
    { $start++; }

  $start--;
  return $start;
  }



# process_spectral_peak_list_frame
#   Processes a spectral peak list frame and creates an organized data hash structure.
#
#   Parameters:
#       $save_frame - save_frame hash
#
sub process_spectral_peak_list_frame
  {
  my $save_frame = shift @_;
  my $bmrb_hlist = shift @_;

  my $sections = [];

  # required and expected fields for loops
  my @expected_dim_description_fields = ("_Spectral_dim.Entry_ID","_Spectral_dim.Spectral_peak_list_ID","_Spectral_dim.Spectral_dim_ID","_Spectral_dim.Atom_type","_Spectral_dim.Spectral_region","_Spectral_dim.Magnetization_linkage_ID","_Spectral_dim.Sweep_width","_Spectral_dim.Encoding_code","_Spectral_dim.Encoded_source_dimension_ID");
  my @required_dim_description_fields = ("_Spectral_dim.Spectral_dim_ID","_Spectral_dim.Atom_type","_Spectral_dim.Spectral_region","_Spectral_dim.Magnetization_linkage_ID","_Spectral_dim.Sweep_width","_Spectral_dim.Encoding_code","_Spectral_dim.Encoded_source_dimension_ID");

  my @expected_peak_fields = ("_Peak.Entry_ID","_Peak.Spectral_peak_list_ID","_Peak.Peak_ID","_Peak.Intensity_val","_Peak.Intensity_err","_Peak.Figure_of_merit","_Peak.Intensity_measurement_methods_ID","_Peak.Derivation_set_ID");
  my @required_peak_fields = ("_Peak.Peak_ID","_Peak.Intensity_val","_Peak.Intensity_err","_Peak.Figure_of_merit","_Peak.Intensity_measurement_methods_ID","_Peak.Derivation_set_ID");

  my @expected_peak_char_fields = ("_Peak_char.Entry_ID","_Peak_char.Spectral_peak_list_ID","_Peak_char.Peak_ID","_Peak_char.Spectral_dimension_ID","_Peak_char.Chem_shift_val","_Peak_char.Chem_shift_val_err","_Peak_char.Line_width_val","_Peak_char.Line_width_val_err","_Peak_char.Phase_val","_Peak_char.Phase_val_err","_Peak_char.Decay_rate_val","_Peak_char.Decay_rate_val_err","_Peak_char.Derivation_methods_ID");
  my @required_peak_char_fields = ("_Peak_char.Peak_ID","_Peak_char.Spectral_dimension_ID","_Peak_char.Chem_shift_val","_Peak_char.Chem_shift_val_err","_Peak_char.Line_width_val","_Peak_char.Line_width_val_err","_Peak_char.Phase_val","_Peak_char.Phase_val_err","_Peak_char.Decay_rate_val","_Peak_char.Decay_rate_val_err","_Peak_char.Derivation_methods_ID");

  my @expected_assignment_fields = ("_Assigned_peak_chem_shift.Entry_ID","_Assigned_peak_chem_shift.Spectral_peak_list_ID","_Assigned_peak_chem_shift.Peak_ID","_Assigned_peak_chem_shift.Set_ID","_Assigned_peak_chem_shift.Magnetization_linkage_ID","_Assigned_peak_chem_shift.Figure_of_merit","_Assigned_peak_chem_shift.Spectral_dimension_ID","_Assigned_peak_chem_shift.List_ID","_Assigned_peak_chem_shift.List_label","_Assigned_peak_chem_shift.Atom_chem_shift_ID");
  my @required_assignment_fields = ("_Assigned_peak_chem_shift.Peak_ID","_Assigned_peak_chem_shift.Set_ID","_Assigned_peak_chem_shift.Magnetization_linkage_ID","_Assigned_peak_chem_shift.Figure_of_merit","_Assigned_peak_chem_shift.Spectral_dimension_ID","_Assigned_peak_chem_shift.List_ID","_Assigned_peak_chem_shift.List_label","_Assigned_peak_chem_shift.Atom_chem_shift_ID");

  my $orig_text = $$save_frame{"text"};
  # foreach line in the file until stop_
  for(my $x=0; $x < @$orig_text; $x++)
    {
    my $columns = 0;

    # process details
    if($$orig_text[$x] =~ /^\s*_Spectral_peak_list\.Details/)
      {
      # skip spaces and comment lines
      while(($x < @$orig_text) && ($$orig_text[$x] !~ /^;/))
	{ $x++; }

      # skip the ";"
      $x++;

      # process to the ";" line
      while(($x < @$orig_text) && ($$orig_text[$x] !~ /^;/))
	{
	push @{$$save_frame{"data"}{"details"}}, $$orig_text[$x];
	$x++;
	}

      push @$sections, { "type" => "details" };
      }
    # process entry ID
    elsif($$orig_text[$x] =~ /^\s*_Spectral_peak_list\.Entry_ID/)
      {
      ($$save_frame{"data"}{"entry_id"}) = ($$orig_text[$x] =~ /\s*_Spectral_peak_list\.Entry_ID\s*(\w+)\s*/);
      
      push @$sections, { "type" => "entry_id" };
      }
    # process spectral peak list ID
    elsif($$orig_text[$x] =~ /^\s*_Spectral_peak_list\.Spectral_peak_list_ID/)
      {
      ($$save_frame{"data"}{"peak_list_id"}) = ($$orig_text[$x] =~ /\s*_Spectral_peak_list\.Spectral_peak_list_ID\s*(\w+)\s*/);
      
      push @$sections, { "type" => "peak_list_id" };
      }
    # process Number of Spectral Dimensions
    elsif($$orig_text[$x] =~ /^\s*_Spectral_peak_list\.Number_of_spectral_dimensions/)
      {
      ($$save_frame{"data"}{"num_dimensions"}) = ($$orig_text[$x] =~ /\s*_Spectral_peak_list\.Number_of_spectral_dimensions\s*(\w+)\s*/);
      
      push @$sections, { "type" => "num_dimensions" };
      }
    # process Experiment label
    elsif($$orig_text[$x] =~ /^\s*_Spectral_peak_list\.Experiment_label/)
      {
      ($$save_frame{"data"}{"experiment_label"}) = ($$orig_text[$x] =~ /\s*_Spectral_peak_list\.Experiment_label\s*(\w+)\s*/);
      
      push @$sections, { "type" => "experiment_label" };
      }
    # process the dimension description loop
    elsif (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_dim_description_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      my @extra_tags = (grep {my $test = $_; (! grep {$test eq $_; } (@expected_dim_description_fields)); } (keys %$columns));
      $x = &process_dimension_description_lines($orig_text,$x,$columns,$save_frame,\@extra_tags);
      push @$sections, { "type" => "dimension_descriptions", "extra_tags" => [@extra_tags], "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    # process the peak loop
    elsif (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_peak_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      my @extra_tags = (grep {my $test = $_; (! grep {$test eq $_; } (@expected_peak_fields)); } (keys %$columns));
      $x = &process_peak_lines($orig_text,$x,$columns,$save_frame,\@extra_tags);
      push @$sections, { "type" => "peaks", "extra_tags" => [@extra_tags], "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    # process the peak characteristics loop
    elsif (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_peak_char_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      my @extra_tags = (grep {my $test = $_; (! grep {$test eq $_; } (@expected_peak_char_fields)); } (keys %$columns));
      $x = &process_peak_characteristics_lines($orig_text,$x,$columns,$save_frame,\@extra_tags);
      push @$sections, { "type" => "peak_characteristics", "extra_tags" => [@extra_tags], "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    # process the assignment loop
    elsif (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_assignment_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      my @extra_tags = (grep {my $test = $_; (! grep {$test eq $_; } (@expected_assignment_fields)); } (keys %$columns));
      $x = &process_peak_assignment_lines($orig_text,$x,$columns,$save_frame,\@extra_tags);
      push @$sections, { "type" => "peak_assignments", "extra_tags" => [@extra_tags], "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    else
      {
      if (! @$sections || ($$sections[$#{$sections}]{"type"} ne "text")) 
	{ push @$sections, { "type" => "text", "value" => [] }; }
      push @{$$sections[$#{$sections}]{"value"}}, $$orig_text[$x];
      }
    }

  if (@$sections)
    { 
    $$save_frame{"sections"} = $sections; 
    delete $$save_frame{"text"}; 
    }
  }

# update_spectral_peak_list_frames
#   Processes the dimension description lines and add them to the save_frame data subhash
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$save_frame - save frame hash
#	$extra_tags - array of extra tags in the column_elements.
sub update_spectral_peak_list_frames
  {
  my $bmrb_hlist = shift @_;
  my $peak_list_frames = shift @_;

  #build shift id list
  my $shift_id_hash = {};
  foreach my $residue (values %{$$bmrb_hlist{"rlist"}})
    {
    foreach my $atom_name (keys %{$$residue{"shifts"}})
      {
      foreach my $id (@{$$residue{"shifts"}{$atom_name}{"idlist"}})
	{
	$$shift_id_hash{$id} = [ $residue, $atom_name ];
	}
      }
    }

  #update spectral_peak_list save frames.
  foreach my $save_frame (@$peak_list_frames)
    {
    foreach my $peak (values %{$$save_frame{"data"}{"plist"}})
      {
      foreach my $dimension (values %{$$peak{"dimensions"}})
	{
	if (exists $$dimension{"assignments"})
	  {
	  foreach my $assignment (@{$$dimension{"assignments"}})
	    {
	    $$assignment{"aa"} = $$shift_id_hash{$$assignment{"shift_id"}}[0]{"aa"};
	    $$assignment{"index"} = $$shift_id_hash{$$assignment{"shift_id"}}[0]{"index"};
	    $$assignment{"atom_name"} = $$shift_id_hash{$$assignment{"shift_id"}}[1];
	    }
	  }
	}
      }
    }
  }

# process_dimension_description_lines
#   Processes the dimension description lines and add them to the save_frame data subhash
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$save_frame - save frame hash
#	$extra_tags - array of extra tags in the column_elements.
sub process_dimension_description_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $save_frame = shift @_;
  my $extra_tags = shift @_;

  $$save_frame{"data"}{"dimension_descriptions"} = [];

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }
    
    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }


    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    my $dimension_description = {};

    $$dimension_description{"dim_id"} = $tokens[$$column_elements{"_Spectral_dim.Spectral_dim_ID"}];
    $$dimension_description{"atom_type"} = $tokens[$$column_elements{"_Spectral_dim.Atom_type"}] if ($tokens[$$column_elements{"_Spectral_dim.Atom_type"}] ne ".");
    $$dimension_description{"spectral_region"} = $tokens[$$column_elements{"_Spectral_dim.Spectral_region"}] if ($tokens[$$column_elements{"_Spectral_dim.Spectral_region"}] ne ".");
    $$dimension_description{"magnetization_linkage"} = $tokens[$$column_elements{"_Spectral_dim.Magnetization_linkage_ID"}] if ($tokens[$$column_elements{"_Spectral_dim.Magnetization_linkage_ID"}] ne ".");
    $$dimension_description{"sweep_width"} = $tokens[$$column_elements{"_Spectral_dim.Sweep_width"}] if ($tokens[$$column_elements{"_Spectral_dim.Sweep_width"}] ne ".");
    $$dimension_description{"encoding"} = $tokens[$$column_elements{"_Spectral_dim.Encoding_code"}] if ($tokens[$$column_elements{"_Spectral_dim.Encoding_code"}] ne ".");
    $$dimension_description{"encoding_dimension"} = $tokens[$$column_elements{"_Spectral_dim.Encoded_source_dimension_ID"}] if ($tokens[$$column_elements{"_Spectral_dim.Encoded_source_dimension_ID"}] ne ".");

       
    foreach my $field (@$extra_tags)
      { 
      if ($tokens[$$column_elements{$field}] ne ".")
	{ $$dimension_description{"extra_tags"}{$field} = $tokens[$$column_elements{$field}]; }
      }

    push @{$$save_frame{"data"}{"dimension_descriptions"}}, $dimension_description;
    }
  continue
    { $start++; }

  $start--;
  return $start;
  }


# process_peak_lines
#   Processes the peak lines and add them to the save_frame data subhash
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$save_frame - save frame hash
#	$extra_tags - array of extra tags in the column_elements.
sub process_peak_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $save_frame = shift @_;
  my $extra_tags = shift @_;

  $$save_frame{"data"}{"plist"} = {};
  $$save_frame{"data"}{"index_array"} = [];

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }
    
    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }


    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    my $peak = {};

    $$peak{"index"} = $tokens[$$column_elements{"_Peak.Peak_ID"}];
    $$peak{"intensity"} = $tokens[$$column_elements{"_Peak.Intensity_val"}] if ($tokens[$$column_elements{"_Peak.Intensity_val"}] ne ".");
    $$peak{"intensity_error"} = $tokens[$$column_elements{"_Peak.Intensity_err"}] if ($tokens[$$column_elements{"_Peak.Intensity_err"}] ne ".");
    $$peak{"figure_of_merit"} = $tokens[$$column_elements{"_Peak.Figure_of_merit"}] if ($tokens[$$column_elements{"_Peak.Figure_of_merit"}] ne ".");
    $$peak{"measurement_method"} = $tokens[$$column_elements{"_Peak.Intensity_measurement_methods_ID"}] if ($tokens[$$column_elements{"_Peak.Intensity_measurement_methods_ID"}] ne ".");
    $$peak{"derivation_set"} = $tokens[$$column_elements{"_Peak.Derivation_set_ID"}] if ($tokens[$$column_elements{"_Peak.Derivation_set_ID"}] ne ".");
       
    foreach my $field (@$extra_tags)
      { 
      if ($tokens[$$column_elements{$field}] ne ".")
	{ $$peak{"extra_tags"}{$field} = $tokens[$$column_elements{$field}]; }
      }

    push @{$$save_frame{"data"}{"index_array"}}, $$peak{"index"};
    $$save_frame{"data"}{"plist"}{$$peak{"index"}} = $peak;
    }
  continue
    { $start++; }

  $start--;
  return $start;
  }

# process_peak_characteristics_lines
#   Processes the peak characteristic lines and add them to the save_frame data subhash
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$save_frame - save frame hash
#	$extra_tags - array of extra tags in the column_elements.
sub process_peak_characteristics_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $save_frame = shift @_;
  my $extra_tags = shift @_;

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }
    
    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }


    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    my $dimension = {};

    my $peak_index = $tokens[$$column_elements{"_Peak_char.Peak_ID"}];
    $$dimension{"dim_id"} = $tokens[$$column_elements{"_Peak_char.Spectral_dimension_ID"}];
    $$dimension{"shift"} = $tokens[$$column_elements{"_Peak_char.Chem_shift_val"}];
    $$dimension{"shift_error"} = $tokens[$$column_elements{"_Peak_char.Chem_shift_val_err"}] if ($tokens[$$column_elements{"_Peak_char.Chem_shift_val_err"}] ne ".");
    $$dimension{"width"} = $tokens[$$column_elements{"_Peak_char.Line_width_val"}] if ($tokens[$$column_elements{"_Peak_char.Line_width_val"}] ne ".");
    $$dimension{"width_error"} = $tokens[$$column_elements{"_Peak_char.Line_width_val_err"}] if ($tokens[$$column_elements{"_Peak_char.Line_width_val_err"}] ne ".");
    $$dimension{"phase"} = $tokens[$$column_elements{"_Peak_char.Phase_val"}] if ($tokens[$$column_elements{"_Peak_char.Phase_val"}] ne ".");
    $$dimension{"phase_error"} = $tokens[$$column_elements{"_Peak_char.Phase_val_err"}] if ($tokens[$$column_elements{"_Peak_char.Phase_val_err"}] ne ".");
    $$dimension{"decay"} = $tokens[$$column_elements{"_Peak_char.Decay_rate_val"}] if ($tokens[$$column_elements{"_Peak_char.Decay_rate_val"}] ne ".");
    $$dimension{"decay_error"} = $tokens[$$column_elements{"_Peak_char.Decay_rate_val_err"}] if ($tokens[$$column_elements{"_Peak_char.Decay_rate_val_err"}] ne ".");
    $$dimension{"derivation_method"} = $tokens[$$column_elements{"_Peak_char.Derivation_methods_ID"}] if ($tokens[$$column_elements{"_Peak_char.Derivation_methods_ID"}] ne ".");
       
    foreach my $field (@$extra_tags)
      { 
      if ($tokens[$$column_elements{$field}] ne ".")
	{ $$dimension{"extra_tags"}{$field} = $tokens[$$column_elements{$field}]; }
      }

    $$save_frame{"data"}{"plist"}{$peak_index}{"dimensions"}{$$dimension{"dim_id"}} = $dimension;
    }
  continue
    { $start++; }

  $start--;
  return $start;
  }

# process_peak_assignment_lines
#   Processes the peak assignment lines and add them to the save_frame data subhash
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$save_frame - save frame hash
#	$extra_tags - array of extra tags in the column_elements.
sub process_peak_assignment_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $save_frame = shift @_;
  my $extra_tags = shift @_;

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }
    
    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }


    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    my $assignment = {};

    my $peak_index = $tokens[$$column_elements{"_Assigned_peak_chem_shift.Peak_ID"}];
    my $dimension_id = $tokens[$$column_elements{"_Assigned_peak_chem_shift.Spectral_dimension_ID"}];
    $$assignment{"set_id"} = $tokens[$$column_elements{"_Assigned_peak_chem_shift.Set_ID"}] if ($tokens[$$column_elements{"_Assigned_peak_chem_shift.Set_ID"}] ne ".");
    $$assignment{"magnetization_linkage"} = $tokens[$$column_elements{"_Assigned_peak_chem_shift.Magnetization_linkage_ID"}] if ($tokens[$$column_elements{"_Assigned_peak_chem_shift.Magnetization_linkage_ID"}] ne ".");
    $$assignment{"figure_of_merit"} = $tokens[$$column_elements{"_Assigned_peak_chem_shift.Figure_of_merit"}] if ($tokens[$$column_elements{"_Assigned_peak_chem_shift.Figure_of_merit"}] ne ".");
    $$assignment{"list_id"} = $tokens[$$column_elements{"_Assigned_peak_chem_shift.List_ID"}] if ($tokens[$$column_elements{"_Assigned_peak_chem_shift.List_ID"}] ne ".");
    $$assignment{"list_label"} = $tokens[$$column_elements{"_Assigned_peak_chem_shift.List_label"}] if ($tokens[$$column_elements{"_Assigned_peak_chem_shift.List_label"}] ne ".");
    $$assignment{"shift_id"} = $tokens[$$column_elements{"_Assigned_peak_chem_shift.Atom_chem_shift_ID"}];
       
    foreach my $field (@$extra_tags)
      { 
      if ($tokens[$$column_elements{$field}] ne ".")
	{ $$assignment{"extra_tags"}{$field} = $tokens[$$column_elements{$field}]; }
      }

    push @{$$save_frame{"data"}{"plist"}{$peak_index}{"dimensions"}{$dimension_id}{"assignments"}}, $assignment;
    }
  continue
    { $start++; }

  $start--;
  return $start;
  }


# process_monomeric_polymer_frame
#   Reads the residue list from a monomeric polymer frame and puts them in the bmrb hash.
#
#   Parameters:
#       $save_frame - save_frame hash
#	$convert_aa_names - converts single letter amino acid names to three letter representations if true.
#	$bmrb_hlist - bmrb hash structure to fill.
#	$save_frame_count - count of save frames of this type
#
sub process_monomeric_polymer_frame
  {
  my $save_frame = shift @_;
  my $convert_aa_names = shift @_;
  my $bmrb_hlist = shift @_;
  my $save_frame_count = shift @_;

  if ($save_frame_count > 1)
    { return 1; }

  # if you do not have a name array then make it a reference to an array.
  if (! exists $$bmrb_hlist{"name_array"})
    { $$bmrb_hlist{"name_array"} = []; }

  my $sections = [];
  my @required_sequence_fields = ("_Residue_seq_code", "_Residue_label");
  my @expected_sequence_fields = ("_Residue_seq_code", "_Residue_label", "_Residue_author_seq_code", "_Insertion_code", "_Segment_definition_code");

  my $orig_text = $$save_frame{"text"};
  # foreach line in the file until stop_
  for(my $x=0; $x < @$orig_text; $x++)
    {
    my $columns = 0;
    # process the chemical shifts
    if (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_sequence_fields)))
      {

	  $x += 1 + scalar(keys %$columns);
	  my @extra_tags = (grep {my $test = $_; (! grep {$test eq $_; } (@expected_sequence_fields)); } (keys %$columns));
	  $x = &process_sequence_loop_lines($orig_text,$x,$columns,$convert_aa_names, $bmrb_hlist, \@extra_tags);
	  push @$sections, { "type" => "sequence_loop", "extra_tags" => [@extra_tags], "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    elsif($$orig_text[$x] =~ /^\s*_Residue_count/)
      {
      my ($value) = ($$orig_text[$x] =~ /\s*_Residue_count\s*(\d+)\s*/);

      if (($value ne ".") && ($value ne ""))
	{ $$bmrb_hlist{"residue_count"} = $value; }

      push @$sections, { "type" => "residue_count" };
      }
    elsif($$orig_text[$x] =~ /^\s*_Mol_type/)
      {
      my ($value) = ($$orig_text[$x] =~ /\s*_Mol_type\s*(\w+)\s*/);

      if (($value ne ".") && ($value ne ""))
	{ $$bmrb_hlist{"molecule_type"} = $value; }

      push @$sections, { "type" => "molecule_type" };
      }
    elsif($$orig_text[$x] =~ /^\s*_Mol_polymer_class/)
      {
      my ($value) = ($$orig_text[$x] =~ /\s*_Mol_polymer_class\s*(\w+)\s*/);

      if ($value ne "protein")
	{
	@$sections = ();
	}
      else
      {
	  if (($value ne ".") && ($value ne ""))
	  { $$bmrb_hlist{"polymer_class"} = $value; }
	  
	  push @$sections, { "type" => "polymer_class" };
      }
      }
    elsif($$orig_text[$x] =~ /^\s*_Name_common/)
      {
      my ($value) = ($$orig_text[$x] =~ /\s*_Name_common\s*\"(.*)\"\s*/);
      if ($value eq "")
        { ($value) = ($$orig_text[$x] =~ /\s*_Name_common\s*\'(.*)\'\s*/); }
      if ($value eq "")
        { ($value) = ($$orig_text[$x] =~ /\s*_Name_common\s*(\w+)\s*/); }


      if (($value ne ".") && ($value ne ""))
	{ $$bmrb_hlist{"common_name"} = $value; }

      push @$sections, { "type" => "common_name" };
      }
    elsif($$orig_text[$x] =~ /^\s*_Mol_residue_sequence/)
      {
      # skip spaces and comment lines
      while(($x < @$orig_text) && ($$orig_text[$x] !~ /^;/))
	{ $x++; }

      # skip the ";"
      $x++;

      # process to the ";" line
      while(($x < @$orig_text) && ($$orig_text[$x] !~ /^;/))
	{
	my ($sequence_part) = ($$orig_text[$x] =~ /^\s*(\w+)\s*/);
	$$bmrb_hlist{"sequence"} .= $sequence_part;
	$x++;
	}
      push @$sections, { "type" => "sequence" };
      }
    else
      {
      if (! @$sections || ($$sections[$#{$sections}]{"type"} ne "text")) 
	{ push @$sections, { "type" => "text", "value" => [] }; }
      push @{$$sections[$#{$sections}]{"value"}}, $$orig_text[$x];
      }
    }
  if (@$sections)
    { 
    $$save_frame{"sections"} = $sections; 
    delete $$save_frame{"text"}; 
    return 1;
    }

  return 0;
  }


# process_sequence_loop_lines
#   Processes the assigned chemical shift lines and puts them in the bmrb hash structure.
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$convert_aa_names - converts single letter amino acid names to three letter representations if true.
#	$bmrb_hlist - bmrb hash structure to fill.
sub process_sequence_loop_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $convert_aa_names = shift @_;
  my $bmrb_hlist = shift @_;
  my $extra_tags = shift @_;

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }

    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split and split it on spaces
    my @tokens = split(/\s+/, $$text_array[$start]);

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }

    while (scalar(@tokens) >= scalar(keys %$column_elements))
      {
      # as long as you have enough tokens to map to the column elements and the residue label is legitimate continue, otherwise next line.
      next if ((@tokens < scalar(keys %$column_elements)) || 
              ((! exists $aa_name_conversion{lc($tokens[$$column_elements{"_Residue_label"}])}) 
            && (! exists $aa_name_conversion2{uc($tokens[$$column_elements{"_Residue_label"}])}))
	       );
      #   print STDERR "AFTER IF\n";
    
      # if you are supposed to convert the name and you can, then do so ...
      if ($convert_aa_names && exists $aa_name_conversion{lc($tokens[$$column_elements{"_Residue_label"}])})
	{ $tokens[$$column_elements{"_Residue_label"}] = $aa_name_conversion{lc($tokens[$$column_elements{"_Residue_label"}])}; }

      # get the residue name, by hook or crook.
      my $residue_name = (exists $$column_elements{"_Residue_seq_code"}) ? $tokens[$$column_elements{"_Residue_label"}] . $tokens[$$column_elements{"_Residue_seq_code"}] : $tokens[$$column_elements{"_Residue_label"}] . $tokens[$$column_elements{"_Residue_author_seq_code"}];
      
      #  print STDERR "YO >> ",$residue_name, "\n"; 
      my $residue;
      
      # if this has already been defined, then skip 
      next if (grep { $_ eq $residue_name; } @{$$bmrb_hlist{"name_array"}});
      
      if (! exists $$bmrb_hlist{"rlist"}{$residue_name})
	{
	# Fill the residue hash reference with useful goodies 
	$residue = {};
	$$residue{"index"} = (exists $$column_elements{"_Residue_seq_code"}) ? $tokens[$$column_elements{"_Residue_seq_code"}] : $tokens[$$column_elements{"_Residue_author_seq_code"}];
	$$residue{"author_index"} = $tokens[$$column_elements{"_Residue_author_seq_code"}] if (exists $$column_elements{"_Residue_author_seq_code"} && ($tokens[$$column_elements{"_Residue_author_seq_code"}] ne "."));
	$$residue{"aa"} = $tokens[$$column_elements{"_Residue_label"}];
	$$residue{"name"} = $$residue{"aa"} . $$residue{"index"};
	$$residue{"author_name"} = $$residue{"aa"} . $$residue{"author_index"} if (exists $$residue{"author_index"});
	$$bmrb_hlist{"rlist"}{$residue_name} = $residue;
	}
      # it already exists, so just reset the stuff that you would essentially re-read
      else
	{ $residue = $$bmrb_hlist{"rlist"}{$residue_name};}
      
      if (exists $$column_elements{"_Insertion_code"} && ($$column_elements{"_Insertion_code"} ne "."))
	{ $$residue{"insertion_code"} = $tokens[$$column_elements{"_Insertion_code"}]; }
      
      if (exists $$column_elements{"_Segment_definition_code"} && ($$column_elements{"_Segment_definition_code"} ne "."))
	{ $$residue{"segment_definition_code"} = $tokens[$$column_elements{"_Segment_definition_code"}]; }
      
      foreach my $field (@$extra_tags)
	{
	if ($tokens[$$column_elements{$field}] ne ".")
	  { $$residue{"extra_tags"}{$field} = $tokens[$$column_elements{$field}]; }
	}

      # add the residue name to the name array.
      push @{$$bmrb_hlist{"name_array"}}, $residue_name;
      }
    continue
      {
      foreach my $junk (keys %$column_elements)
	{ shift @tokens; }
      }
    }
  continue
    { $start++; }

  $start--;
  return $start;
  }


# process_chemical_shift_reference_frame
#   Reads the chemical shift reference from a bmrb file and puts them in the bmrb hash structure.
#
#   Parameters:
#       $save_frame - save_frame hash
#	$bmrb_hlist - bmrb hash structure to fill.
#
sub process_chemical_shift_reference_frame
  {
  my $save_frame = shift @_;
  my $bmrb_hlist = shift @_;

  my $sections = [];
  my @required_chem_shift_fields = ("_Atom_type", "_Chem_shift_value");

  my $orig_text = $$save_frame{"text"};
  # foreach line in the file until stop_
  for(my $x=0; $x < @$orig_text; $x++)
    {
    my $columns = 0;
    # process the chemical shifts
    if (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_chem_shift_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      $x = &process_reference_lines($orig_text,$x,$columns,$bmrb_hlist);
      push @$sections, { "type" => "reference_loop", "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    else
      {
      if (! @$sections || ($$sections[$#{$sections}]{"type"} ne "text")) 
	{ push @$sections, { "type" => "text", "value" => [] }; }
      push @{$$sections[$#{$sections}]{"value"}}, $$orig_text[$x];
      }
    }

  if (@$sections)
    { 
    $$save_frame{"sections"} = $sections; 
    delete $$save_frame{"text"}; 
    }
  }

# process_reference_lines
#   Processes the assigned chemical shift reference lines and puts them in the bmrb hash structure.
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$bmrb_hlist - bmrb hash structure to fill.
sub process_reference_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $bmrb_hlist = shift @_;

  $$bmrb_hlist{"reference_fields"} = [ sort { $$column_elements{$a} <=> $$column_elements{$b}; } (keys %$column_elements) ];
  my @junk = grep { $$bmrb_hlist{"reference_fields"}[$_] =~ /_[Cc]hem_[Ss]hift_[Vv]alue/; } (0..$#{$$bmrb_hlist{"reference_fields"}});

  if (@junk)
    { $$bmrb_hlist{"reference_shift_index"} = pop @junk; }

  while($start < @$text_array)
    {
    # stop on the stop_
    if ($$text_array[$start] =~ /^\s*stop_/)
      { $start++; last; }

    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split on spaces
    my @tokens = grep { (defined $_) && ($_ ne "") ; } (split(/\s+|(\'.*\')/, $$text_array[$start]));

    # get rid of the null element in the beginning caused by the spaces followed by characters
    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }

    # strip away single quote marks
    foreach my $token (@tokens)
      { $token =~ tr/\'//d; }

    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));

    $$bmrb_hlist{"reference"}{$tokens[$$column_elements{"_Atom_type"}]} = [@tokens];
    } 
  continue
    { $start++; }

  $start--;
  return $start;
  }



# process_coupling_constants_frame
#   Reads the coupling constants from a bmrb file and puts them in the bmrb hash structure.
#
#   Parameters:
#       $save_frame - save_frame hash
#	$bmrb_hlist - bmrb hash structure to fill.
#
sub process_coupling_constants_frame
  {
  my $save_frame = shift @_;
  my $bmrb_hlist = shift @_;

  my $sections = [];
  my @coupling_constants_fields_hlist = (values %$coupling_constants_saveframe);
  my @required_coupling_constants_fields = grep {if ($$_{"required"}) { $_ = $$_{"bmrbFieldName"}}} @coupling_constants_fields_hlist;
  undef @coupling_constants_fields_hlist;
  
  my $orig_text = $$save_frame{"text"};
  # foreach line in the file until stop_
  for(my $x=0; $x < @$orig_text; $x++)
    {
    my $columns = 0;
    # process the chemical shifts
    if (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_coupling_constants_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      $x = &process_coupling_constants_lines($orig_text,$x,$columns,$save_frame);
      push @$sections, { "type" => "coupling_constants", "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    else
      {
      if (! @$sections || ($$sections[$#{$sections}]{"type"} ne "text")) 
	{ push @$sections, { "type" => "text", "value" => [] }; }
      push @{$$sections[$#{$sections}]{"value"}}, $$orig_text[$x];
      }
    }

  if (@$sections)
    { 
    $$save_frame{"sections"} = $sections; 
    delete $$save_frame{"text"}; 
    }
  }


# process_coupling_constants_lines
#   Processes the coupling constants lines and puts them in the bmrb hash structure.
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$bmrb_hlist - bmrb hash structure to fill.
sub process_coupling_constants_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $save_frame = shift @_;

  while($start < @$text_array)
    {
    # stop on the stop_
    last if ($$text_array[$start] =~ /^\s*stop_/);

    # skip comment or blank lines
    next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));

    # split on spaces
    my @tokens = grep { (defined $_) && ($_ ne ""); } (split(/\s+|(\'.*\')/, $$text_array[$start]));

    # get rid of the null element in the beginning caused by the spaces followed by characters

    while (@tokens && ($tokens[0] eq ""))
      { shift @tokens; }

    # strip away single quote marks
    foreach my $token (@tokens)
      { $token =~ tr/\'//d; }

    # skip lines which do not have enough columns.
    next if (scalar(@tokens) < scalar(keys %$column_elements));
   
    my $count = 0;

#    print "YO >> ", join(":", @tokens), "\n";
#    $dumper->dumpValue($column_elements);
#    print "YO \n";
    if (exists $$save_frame{"data"}{"cclist"})
    {
	$count = @{$$save_frame{"data"}{"cclist"}};   
    }

    foreach my $key (keys %$coupling_constants_saveframe)
    {
	my $bmrbFieldName = $$coupling_constants_saveframe{$key}{"bmrbFieldName"};
	if ((exists $$column_elements{$bmrbFieldName}) && 
	    (@tokens > $$column_elements{$bmrbFieldName}) && 
	    ($tokens[$$column_elements{$bmrbFieldName}] ne ".")) 
        {
	    $ {$$save_frame{"data"}{"cclist"}}[$count]{$key} = $tokens[$$column_elements{$bmrbFieldName}];
	}
    }

#    my $cc_elem = $ {$$save_frame{"data"}{"cclist"}}[$count];
#    $dumper->dumpValue($cc_elem);
    } 
  continue
    { $start++; }

  $start--;
  return $start;
  }





# process_secondary_structure_frame
#   Reads the secondary structure from a bmrb file and puts them in the bmrb hash structure.
#
#   Parameters:
#       $save_frame - save_frame hash
#	$bmrb_hlist - bmrb hash structure to fill.
#
sub process_secondary_structure_frame
  {
  my $save_frame = shift @_;
  my $bmrb_hlist = shift @_;

  my $sections = [];
  my @secondary_structure_fields_hlist = (values %$secondary_structure_saveframe);
  my @required_secondary_structure_fields = grep {if ($$_{"required"}) { $_ = $$_{"bmrbFieldName"}}} @secondary_structure_fields_hlist;
  undef @secondary_structure_fields_hlist;



  my $orig_text = $$save_frame{"text"};
  # foreach line in the file until stop_
  for(my $x=0; $x < @$orig_text; $x++)
    {
    my $columns = 0;
    # process the chemical shifts
    if (($$orig_text[$x] =~ /^\s*loop_/) && ($columns = &loop_contents($orig_text,$x+1,\@required_secondary_structure_fields)))
      {
      $x += 1 + scalar(keys %$columns);
      $x = &process_secondary_structure_lines($orig_text,$x,$columns,$save_frame);
      push @$sections, { "type" => "secondary_structure", "all_tags" => [ sort { $$columns{$a} <=> $$columns{$b}; } (keys %$columns) ] };
      }
    else
      {
      if (! @$sections || ($$sections[$#{$sections}]{"type"} ne "text")) 
	{ push @$sections, { "type" => "text", "value" => [] }; }
      push @{$$sections[$#{$sections}]{"value"}}, $$orig_text[$x];
      }
    }

  if (@$sections)
    { 
    $$save_frame{"sections"} = $sections; 
    delete $$save_frame{"text"}; 
    }
  }


# process_secondary_structure_lines
#   Processes the secondary structure lines and puts them in the bmrb hash structure.
#	Returns the ending line index.
#
#   Parameters:
#       $text_array - array of text lines to process
#	$start - starting index in the array.
#	$column_elements - hash of fields to column positions.
#	$bmrb_hlist - bmrb hash structure to fill.
sub process_secondary_structure_lines
  {
  my $text_array = shift @_;
  my $start = shift @_;
  my $column_elements = shift @_;
  my $save_frame = shift @_;

  while($start < @$text_array)
    {
    # stop on the stop_
	last if ($$text_array[$start] =~ /^\s*stop_/);

    # skip comment or blank lines
	next if (($$text_array[$start] =~ /^\s*\#/) || ($$text_array[$start] =~ /^\s*$/));
	
	# split on spaces
	my @tokens = grep { (defined $_) && ($_ ne ""); } (split(/\s+|(\'.*\')/, $$text_array[$start]));
	
	# get rid of the null element in the beginning caused by the spaces followed by characters
	
	while (@tokens && ($tokens[0] eq ""))
	{ shift @tokens; }
	
	# strip away single quote marks
	foreach my $token (@tokens)
	{ $token =~ tr/\'//d; }
	
	# skip lines which do not have enough columns.
	next if (scalar(@tokens) < scalar(keys %$column_elements));
	
	my $count = 0;
	
#    print "YO >> ", join(":", @tokens), "\n";
#    $dumper->dumpValue($column_elements);
#    print "YO \n";
	if (exists $$save_frame{"data"}{"sslist"})
	   {$count = @{$$save_frame{"data"}{"sslist"}};}

	foreach my $key (keys %$secondary_structure_saveframe)
	{
	    my $bmrbFieldName = $$secondary_structure_saveframe{$key}{"bmrbFieldName"};
	    if ((exists $$column_elements{$bmrbFieldName}) && 
		(@tokens > $$column_elements{$bmrbFieldName}) && 
		($tokens[$$column_elements{$bmrbFieldName}] ne ".")) 
	    {
		$ {$$save_frame{"data"}{"sslist"}}[$count]{$key} = $tokens[$$column_elements{$bmrbFieldName}];
	    }
	}  
    } 
  continue
  { $start++; }
  
  $start--;
  return $start;
}



#################################### residueNameofIndex #######################################
# Input   : a bmrb_hlist type and an index                                                    # 
# Output  : the residue name of the residue with that index                                   #
# Purpose : To return the residue name of the residue with the inputted index                 #
###############################################################################################
sub residueNameofIndex
{
    my $bmrb_hlist = shift @_;
    my $index = shift @_;

    foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
    {
	$residue_name =~ /^([A-Za-z]+)(\d+)$/;
	if ($2 == $index)
	{
	    return $residue_name;
	}
    }
    return "";
}


#
#
#      Routines for writing BMRB files
#
#


# has_shifts
#   Returns true if the given bmrb hash structure has any shifts
#
#   Parameters:
#	$bmrb_hlist - reference to bmrb hash structure.
sub has_shifts
  {
  my $bmrb_hlist = shift @_;
  
  if (exists $$bmrb_hlist{"rlist"})
    {
    foreach my $residue (values %{$$bmrb_hlist{"rlist"}})
      {
      if (exists $$residue{"shifts"})
	{
	foreach my $shift (values %{$$residue{"shifts"}})
	  {
	  if (exists $$shift{"list"} && @{$$shift{"list"}})
	    { return 1; }
	  }
	}
      } 
    }

  return 0;
  }


# update_shift_ids
#   updates the shift ids in the bmrb hash structure.
#
#   Parameters:
#	$bmrb_hlist - ref to bmrb hash structure.
#
sub update_shift_ids
  {
  my $bmrb_hlist = shift @_;

  if (&has_shifts($bmrb_hlist))
    {
    my $id_conversion_hash = {};
    my $shift_id = 1;

    # update assigned chemical shifts
    foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
    {
	my $residue = $$bmrb_hlist{"rlist"}{$residue_name};
	foreach my $shift_name (sort { $BMRB_shift_name_ordering{$a} <=> $BMRB_shift_name_ordering{$b}; } (keys %{$$residue{"shifts"}}))
	{
	    if (exists $$residue{"shifts"}{$shift_name}{"list"})
	    {
		for(my $x=0; $x < @{$$residue{"shifts"}{$shift_name}{"list"}}; $x++)
		{
		    if (! exists $$residue{"shifts"}{$shift_name}{"idlist"} || (ref $$residue{"shifts"}{$shift_name}{"idlist"} ne "ARRAY"))
		    { $$residue{"shifts"}{$shift_name}{"idlist"} = []; }
		    
		    if ($x < @{$$residue{"shifts"}{$shift_name}{"idlist"}})
		    { $$id_conversion_hash{$$residue{"shifts"}{$shift_name}{"idlist"}[$x]} = $shift_id; }
		    
		    $$residue{"shifts"}{$shift_name}{"idlist"}[$x] = $shift_id;
		    $shift_id++;
		}
	    }
	}
    }
    
    # update ambiguous settings
    if (exists $$bmrb_hlist{"ambiguity_lists"} && @{$$bmrb_hlist{"ambiguity_lists"}})
      {
      foreach my $ambiguity_list (@{$$bmrb_hlist{"ambiguity_lists"}})
	{
	foreach my $shift_id (@$ambiguity_list)
	  {
	  if (exists $$id_conversion_hash{$shift_id})
	    { $shift_id = $$id_conversion_hash{$shift_id}; }
	  }
	}
      }

    # update peak assignments
    if (exists $$bmrb_hlist{"save_frames"} && (ref $$bmrb_hlist{"save_frames"} eq "ARRAY"))
      {
      foreach my $save_frame (grep {$$_{"type"} eq "spectral_peak_list"; } (@{$$bmrb_hlist{"save_frames"}}))
	{
	if (exists $$save_frame{"data"} && $$save_frame{"data"}{"plist"})
	  {
	  foreach my $peak (values %{$$save_frame{"data"}{"plist"}})
	    {
	    foreach my $dimension (values %{$$peak{"dimensions"}})
	      {
	      if (exists $$dimension{"assignments"})
		{
		foreach my $assignment (@{$$dimension{"assignments"}})
		  {
		  if (exists $$assignment{"shift_id"} && exists $$id_conversion_hash{$$assignment{"shift_id"}})
		    { $$assignment{"shift_id"} = $$id_conversion_hash{$$assignment{"shift_id"}}; }
		  }
		}
	      }
	    }
	  }
	}
      }

    }
  
  }

# create_assigned_chemical_shifts_frame
#   Returns an array of text_lines given the description of the save_frame and the bmrb hash structure.
#
#   Parameters:
#	$save_frame - ref to save_frame hash structure.
#	$bmrb_hlist - ref to bmrb hash structure.
#	$write_options - ref to write options hash
#
sub create_assigned_chemical_shifts_frame
 {
 my $save_frame = shift @_;
 my $bmrb_hlist = shift @_;
 my $write_options = shift @_;

 my $text_lines = [];

 foreach my $section (@{$$save_frame{"sections"}})
   {
   if ($$section{"type"} eq "text")
     { 
     push @$text_lines, (@{$$section{"value"}}); 
     }
   elsif ($$section{"type"} eq "chemical_shifts_loop")
     {
     push @$text_lines, "   loop_";
     foreach my $tag ("_Atom_shift_assign_ID", "_Residue_seq_code", "_Residue_label", "_Atom_name", "_Atom_type", "_Chem_shift_value", "_Chem_shift_value_error", "_Chem_shift_ambiguity_code")
       { push @$text_lines, "     " . $tag; }

     if (exists $$section{"extra_tags"} && @{$$section{"extra_tags"}})
       {
       foreach my $tag (@{$$section{"extra_tags"}})
	 { push @$text_lines, "     " . $tag; }
       }

     push @$text_lines, "";

     foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
       {
       my $residue = $$bmrb_hlist{"rlist"}{$residue_name};
       foreach my $shift_name (sort { $BMRB_shift_name_ordering{$a} <=> $BMRB_shift_name_ordering{$b}; } (keys %{$$residue{"shifts"}}))
       {
	   if (exists $$residue{"shifts"}{$shift_name}{"list"})
	   {
	       for(my $x=0; $x < @{$$residue{"shifts"}{$shift_name}{"list"}}; $x++)
	       {
		   # build shift line
		   my $shift_line = $$residue{"shifts"}{$shift_name}{"idlist"}[$x] . "	" . $$residue{"index"} . "	" . $aa_name_conversion2{uc($$residue{"aa"})} .  " " .
		       $shift_name . "	" . $$residue{"shifts"}{$shift_name}{"atom_type"} . "	" . $$residue{"shifts"}{$shift_name}{"list"}[$x] ;
		   if (exists $$residue{"shifts"}{$shift_name}{"error"})
		   { $shift_line .= "	" . $$residue{"shifts"}{$shift_name}{"error"}; }
		   else
		   { $shift_line .= "	."; }
		   if (exists $$residue{"shifts"}{$shift_name}{"ambiguity_code"} && $$residue{"shifts"}{$shift_name}{"ambiguity_code"})
		   { $shift_line .= "	" . $$residue{"shifts"}{$shift_name}{"ambiguity_code"}; }
		   else
		   { $shift_line .= "	1"; }
		   
		   # add extra tags if they exist
		   my @extra_tags;
		   if (exists $$section{"extra_tags"} && @{$$section{"extra_tags"}})
		   { @extra_tags = @{$$section{"extra_tags"}}; }
		   if (exists $$write_options{"extra_tags"} && exists $$write_options{"extra_tags"}{"shifts"})
		   { @extra_tags = @{$$write_options{"extra_tags"}{"shifts"}}; }
		   
		   if (@extra_tags)
		   {
		       foreach my $tag (@extra_tags)
		       {
			   if (exists $$residue{"shifts"}{$shift_name}{"extra_tags"} && exists $$residue{"shifts"}{$shift_name}{"extra_tags"}{$tag})
			   { $shift_line .=  "     " . $$residue{"shifts"}{$shift_name}{"extra_tags"}{$tag}; }
			   else
			   { $shift_line .=  "     ."; }
		       }
		   }
		   
		   # add shift line
		   push @$text_lines,  $shift_line;
	       }
	   }
       }
   }

     push @$text_lines, "","   stop_";
     }
   elsif ($$section{"type"} eq "ambiguity_loop")
     {
     if (exists $$bmrb_hlist{"ambiguity_lists"} && @{$$bmrb_hlist{"ambiguity_lists"}})
       {
       push @$text_lines, "   loop_", "     _Atom_shift_assign_ID_ambiguity", "";
       foreach my $ambiguity_list (@{$$bmrb_hlist{"ambiguity_lists"}})
	 {
	 my $ambiguity = "";
	 foreach my $id (@$ambiguity_list)
	   {
	   if ($ambiguity eq "")
	     { $ambiguity = $id; }
	   else
	     { $ambiguity .= "," . $id; }
	   }
	 
	 if ($ambiguity ne "")
	   { push @$text_lines, $ambiguity; }
	 }

       push @$text_lines, "","   stop_";
       }
     }
   }

 return $text_lines;
 }

# create_monomeric_polymer_frame
#   Returns an array of text_lines given the description of the save_frame and the bmrb hash structure.
#
#   Parameters:
#	$save_frame - ref to save_frame hash structure.
#	$bmrb_hlist - ref to bmrb hash structure.
#
sub create_monomeric_polymer_frame
 {
 my $save_frame = shift @_;
 my $bmrb_hlist = shift @_;

 my $text_lines = [];

 foreach my $section (@{$$save_frame{"sections"}})
   {
   if ($$section{"type"} eq "text")
     { 
     push @$text_lines, (@{$$section{"value"}}); 
     }
   elsif ($$section{"type"} eq "molecule_type")
     {
     if (exists $$bmrb_hlist{"molecule_type"} && ($$bmrb_hlist{"molecule_type"} ne ""))
       { push @$text_lines, "    _Mol_type	" . $$bmrb_hlist{"molecule_type"}; }
     else
       { push @$text_lines, "    _Mol_type	."; }
     }
   elsif ($$section{"type"} eq "polymer_class")
     {
     if (exists $$bmrb_hlist{"polymer_class"} && ($$bmrb_hlist{"polymer_class"} ne ""))
       { push @$text_lines, "    _Mol_polymer_class	" . $$bmrb_hlist{"polymer_class"}; }
     else
       { push @$text_lines, "    _Mol_polymer_class	."; }
     }
   elsif ($$section{"type"} eq "common_name" )
     {
     if (exists $$bmrb_hlist{"common_name"} && ($$bmrb_hlist{"common_name"} ne ""))
       {
       if ($$bmrb_hlist{"common_name"} =~ /\s+/)
	 { push @$text_lines, "    _Name_common	\'" . $$bmrb_hlist{"common_name"} . "\'"; }
       else
	 { push @$text_lines, "    _Name_common	" . $$bmrb_hlist{"common_name"}; }
       }
     else
       { push @$text_lines, "    _Name_common	."; }
     }
   elsif ($$section{"type"} eq "residue_count")
     {
     if (exists $$bmrb_hlist{"residue_count"} && ($$bmrb_hlist{"residue_count"} ne ""))
       { push @$text_lines, "    _Residue_count	" . $$bmrb_hlist{"residue_count"}; }
     else
       { push @$text_lines, "    _Residue_count	."; }
     }
   elsif ($$section{"type"} eq "sequence")
     {
     push @$text_lines, "    _Mol_residue_sequence", ";";
     if (exists $$bmrb_hlist{"sequence"} && ($$bmrb_hlist{"sequence"} ne ""))
       {
       for(my $x=0; $x < length($$bmrb_hlist{"sequence"}); $x += 10)
	 {
	 if (($x + 10) > length($$bmrb_hlist{"sequence"}))
	   { push @$text_lines, substr($$bmrb_hlist{"sequence"}, $x); }
	 else
	   { push @$text_lines, substr($$bmrb_hlist{"sequence"}, $x, 10); }
	 }
       }
     else
       { push @$text_lines, "."; }

     push @$text_lines, ";";
     }
   elsif ($$section{"type"} eq "sequence_loop")
     {
     push @$text_lines, "   loop_";
     foreach my $tag (@{$$section{"all_tags"}})
       { push @$text_lines, "     " . $tag; }

     push @$text_lines, "";

     foreach my $residue_name (@{$$bmrb_hlist{"name_array"}})
       {
       my $residue = $$bmrb_hlist{"rlist"}{$residue_name};
       my $residue_line = "";

       foreach my $tag (@{$$section{"all_tags"}})
	 { 
	 if ($tag eq "_Residue_seq_code")
	   { 
	   if (exists $$residue{"index"}) 
	     { $residue_line .= "	" . $$residue{"index"}; }
	   else
	     { $residue_line .= "	."; }
	   }
	 elsif ($tag eq "_Residue_author_seq_code")
	   { 
	   if (exists $$residue{"author_index"}) 
	     { $residue_line .= "	" . $$residue{"author_index"}; }
	   else
	     { $residue_line .= "	."; }
	   }
	 elsif ($tag eq "_Insertion_code")
	   { 
	   if (exists $$residue{"insertion_code"}) 
	     { $residue_line .= "	" . $$residue{"insertion_code"}; }
	   else
	     { $residue_line .= "	."; }
	   }
	 elsif ($tag eq "_Residue_label")
	   { 
	   if (exists $$residue{"aa"}) 
	     { $residue_line .= "	" . $aa_name_conversion2{uc($$residue{"aa"})}; }
	   else
	     { $residue_line .= "	."; }
	   }
	 elsif ($tag eq "_Segment_definition_code")
	   { 
	   if (exists $$residue{"segment_definition_code"}) 
	     { $residue_line .= "	" . $$residue{"segment_definition_code"}; }
	   else
	     { $residue_line .= "	."; }
	   }
	 else
	   {
	   if (exists $$residue{"extra_tags"} && exists $$residue{"extra_tags"}{$tag})
	     { $residue_line .= "	" . $$residue{"extra_tags"}{$tag}; }
	   else
	     { $residue_line .= "	."; }
	   }
	 }

       push @$text_lines, $residue_line;
       }

     push @$text_lines, "","   stop_";
     }
   }

 return $text_lines;
 }


# create_spectral_peak_list_frame
#   Returns an array of text_lines given the description of the save_frame and the bmrb hash structure.
#
#   Parameters:
#	$save_frame - ref to save_frame hash structure.
#	$bmrb_hlist - ref to bmrb hash structure.
#
sub create_spectral_peak_list_frame
 {
 my $save_frame = shift @_;
 my $bmrb_hlist = shift @_;


 my $dimension_descriptions_tag_converter = {"_Spectral_dim.Spectral_dim_ID" => "dim_id", "_Spectral_dim.Atom_type" => "atom_type", "_Spectral_dim.Spectral_region" => "spectral_region", "_Spectral_dim.Magnetization_linkage_ID" => "magnetization_linkage", "_Spectral_dim.Sweep_width" => "sweep_width", "_Spectral_dim.Encoding_code" => "encoding", "_Spectral_dim.Encoded_source_dimension_ID" => "encoding_dimension"};

 my $peak_tag_converter = { "_Peak.Peak_ID" => "index", "_Peak.Intensity_val" => "intensity", "_Peak.Intensity_err" => "intensity_error", "_Peak.Figure_of_merit" => "figure_of_merit", "_Peak.Intensity_measurement_methods_ID" => "measurement_method", "_Peak.Derivation_set_ID" => "derivation_set" };

 my $peak_characteristics_tag_converter = { "_Peak_char.Spectral_dimension_ID" => "dim_id", "_Peak_char.Chem_shift_val" => "shift", "_Peak_char.Chem_shift_val_err" => "shift_error", "_Peak_char.Line_width_val" => "width", "_Peak_char.Line_width_val_err" => "width_error", "_Peak_char.Phase_val" => "phase", "_Peak_char.Phase_val_err" => "phase_error", "_Peak_char.Decay_rate_val" => "decay", "_Peak_char.Decay_rate_val_err" => "decay_error", "_Peak_char.Derivation_methods_ID" => "derivation_method" };

 my $peak_assignments_tag_converter = { "_Assigned_peak_chem_shift.Set_ID" => "set_id", "_Assigned_peak_chem_shift.Magnetization_linkage_ID" => "magnetization_linkage", "_Assigned_peak_chem_shift.Figure_of_merit" => "figure_of_merit", "_Assigned_peak_chem_shift.List_ID" => "list_id", "_Assigned_peak_chem_shift.List_label" => "list_label", "_Assigned_peak_chem_shift.Atom_chem_shift_ID" => "shift_id" };

 my $text_lines = [];

 foreach my $section (@{$$save_frame{"sections"}})
   {
       if ($$section{"type"} eq "text")
       { push @$text_lines, (@{$$section{"value"}}); }
       elsif ($$section{"type"} eq "details")
       {
	   push @$text_lines, "    _Spectral_peak_list.Details", ";";
	   if (exists $$save_frame{"data"}{"details"} && ($$save_frame{"data"}{"details"} ne ""))
	   { push @$text_lines, @{$$save_frame{"data"}{"details"}}; }
	   push @$text_lines, ";";
       }
       elsif ($$section{"type"} eq "entry_id")
       {
	   if (exists $$save_frame{"data"}{"entry_id"} && ($$save_frame{"data"}{"entry_id"} ne ""))
	   { push @$text_lines, "    _Spectral_peak_list.Entry_ID	" . $$save_frame{"data"}{"entry_id"}; }
	   else
	   { push @$text_lines, "    _Spectral_peak_list.Entry_ID	."; }       }
       elsif ($$section{"type"} eq "peak_list_id" )
       {
	   if (exists $$save_frame{"data"}{"peak_list_id"} && ($$save_frame{"data"}{"peak_list_id"} ne ""))
	   { push @$text_lines, "    _Spectral_peak_list.Spectral_peak_list_ID	" . $$save_frame{"data"}{"peak_list_id"}; }
	   else
	   { push @$text_lines, "    _Spectral_peak_list.Spectral_peak_list_ID	."; }
       }
       elsif ($$section{"type"} eq "num_dimensions")
       {
	   if (exists $$save_frame{"data"}{"num_dimensions"} && ($$save_frame{"data"}{"num_dimensions"} ne ""))
	   { push @$text_lines, "    _Spectral_peak_list.Number_of_spectral_dimensions	" . $$save_frame{"data"}{"num_dimensions"}; }
	   else
	   { push @$text_lines, "    _Spectral_peak_list.Number_of_spectral_dimensions	."; }
       }
       elsif ($$section{"type"} eq "experiment_label")
       {
	   if (exists $$save_frame{"data"}{"experiment_label"} && ($$save_frame{"data"}{"experiment_label"} ne ""))
	   { push @$text_lines, "    _Spectral_peak_list.Experiment_label	" . $$save_frame{"data"}{"experiment_label"}; }
	   else
	   { push @$text_lines, "    _Spectral_peak_list.Experiment_label	."; }
       }
       elsif ($$section{"type"} eq "dimension_descriptions")
       {
	   push @$text_lines, "   loop_";
	   foreach my $tag (@{$$section{"all_tags"}})
	   { push @$text_lines, "     " . $tag; }
	   
	   push @$text_lines, "";
	   
	   # create direct name-pair hash
	   my $value_hash = {};
	   
	   if (exists $$save_frame{"data"}{"entry_id"} && ($$save_frame{"data"}{"entry_id"} ne ""))
	   { $$value_hash{"_Spectral_dim.Entry_ID"} = $$save_frame{"data"}{"entry_id"}; }
	   else
	   { $$value_hash{"_Spectral_dim.Entry_ID"} = "."; }
	   
	   if (exists $$save_frame{"data"}{"peak_list_id"} && ($$save_frame{"data"}{"peak_list_id"} ne ""))
	   { $$value_hash{"_Spectral_dim.Spectral_peak_list_ID"} = $$save_frame{"data"}{"peak_list_id"}; }
	   else
	   { $$value_hash{"_Spectral_dim.Spectral_peak_list_ID"} = "."; }
	   
	   foreach my $dim_description (@{$$save_frame{"data"}{"dimension_descriptions"}})
	   { push @$text_lines, &create_loop_line($$section{"all_tags"},$value_hash,$dimension_descriptions_tag_converter,$dim_description); }
	   
	   push @$text_lines, "","   stop_";
       }
       elsif ($$section{"type"} eq "peaks")
       {
	   push @$text_lines, "   loop_";
	   foreach my $tag (@{$$section{"all_tags"}})
	   { push @$text_lines, "     " . $tag; }
	   
	   push @$text_lines, "";
	   
	   # create direct name-pair hash
	   my $value_hash = {};
	   
	   if (exists $$save_frame{"data"}{"entry_id"} && ($$save_frame{"data"}{"entry_id"} ne ""))
	   { $$value_hash{"_Peak.Entry_ID"} = $$save_frame{"data"}{"entry_id"}; }
	   else
	   { $$value_hash{"_Peak.Entry_ID"} = "."; }
	   
	   if (exists $$save_frame{"data"}{"peak_list_id"} && ($$save_frame{"data"}{"peak_list_id"} ne ""))
	   { $$value_hash{"_Peak.Spectral_peak_list_ID"} = $$save_frame{"data"}{"peak_list_id"}; }
	   else
	   { $$value_hash{"_Peak.Spectral_peak_list_ID"} = "."; }
	   
	   foreach my $peak_index (@{$$save_frame{"data"}{"index_array"}})
	   { push @$text_lines, &create_loop_line($$section{"all_tags"},$value_hash,$peak_tag_converter,$$save_frame{"data"}{"plist"}{$peak_index}); }
	   
	   push @$text_lines, "","   stop_";
       }
       elsif ($$section{"type"} eq "peak_characteristics")
       {
	   push @$text_lines, "   loop_";
	   foreach my $tag (@{$$section{"all_tags"}})
	   { push @$text_lines, "     " . $tag; }
	   
	   push @$text_lines, "";
	   
	   # create direct name-pair hash
	   my $value_hash = {};
	   
	   if (exists $$save_frame{"data"}{"entry_id"} && ($$save_frame{"data"}{"entry_id"} ne ""))
	   { $$value_hash{"_Peak_char.Entry_ID"} = $$save_frame{"data"}{"entry_id"}; }
	   else
	   { $$value_hash{"_Peak_char.Entry_ID"} = "."; }
	   
	   if (exists $$save_frame{"data"}{"peak_list_id"} && ($$save_frame{"data"}{"peak_list_id"} ne ""))
	   { $$value_hash{"_Peak_char.Spectral_peak_list_ID"} = $$save_frame{"data"}{"peak_list_id"}; }
	   else
	   { $$value_hash{"_Peak_char.Spectral_peak_list_ID"} = "."; }
	   
	   foreach my $peak_index (@{$$save_frame{"data"}{"index_array"}})
	   {
	       my $peak = $$save_frame{"data"}{"plist"}{$peak_index};
	       $$value_hash{"_Peak_char.Peak_ID"} = $$peak{"index"};
	       
	       foreach my $dimension (values %{$$peak{"dimensions"}})
	       { push @$text_lines, &create_loop_line($$section{"all_tags"},$value_hash,$peak_characteristics_tag_converter,$dimension); }
	       
	       push @$text_lines, "#";
	   }
	   push @$text_lines, "","   stop_";
       }
       elsif ($$section{"type"} eq "peak_assignments")
       {
	   push @$text_lines, "   loop_";
	   foreach my $tag (@{$$section{"all_tags"}})
	   { push @$text_lines, "     " . $tag; }
	   
	   push @$text_lines, "";
	   
	   # create direct name-pair hash
	   my $value_hash = {};
	   
	   if (exists $$save_frame{"data"}{"entry_id"} && ($$save_frame{"data"}{"entry_id"} ne ""))
	   { $$value_hash{"_Assigned_peak_chem_shift.Entry_ID"} = $$save_frame{"data"}{"entry_id"}; }
	   else
	   { $$value_hash{"_Assigned_peak_chem_shift.Entry_ID"} = "."; }
	   
	   if (exists $$save_frame{"data"}{"peak_list_id"} && ($$save_frame{"data"}{"peak_list_id"} ne ""))
	   { $$value_hash{"_Assigned_peak_chem_shift.Spectral_peak_list_ID"} = $$save_frame{"data"}{"peak_list_id"}; }
	   else
	   { $$value_hash{"_Assigned_peak_chem_shift.Spectral_peak_list_ID"} = "."; }
	   
	   foreach my $peak_index (@{$$save_frame{"data"}{"index_array"}})
	   {
	       my $peak = $$save_frame{"data"}{"plist"}{$peak_index};
	       $$value_hash{"_Assigned_peak_chem_shift.Peak_ID"} = $$peak{"index"};
	       
	       foreach my $dim_id (sort { $a <=> $b; } (keys %{$$peak{"dimensions"}}))
	       {
		   my $dimension = $$peak{"dimensions"}{$dim_id};
		   $$value_hash{"_Assigned_peak_chem_shift.Spectral_dimension_ID"} = $$dimension{"dim_id"};
		   
		   foreach my $assignment (@{$$dimension{"assignments"}})
		   { push @$text_lines, &create_loop_line($$section{"all_tags"},$value_hash,$peak_assignments_tag_converter,$assignment); }
	       }
	       
	       push @$text_lines, "#";
	   }
	   
	   push @$text_lines, "","   stop_";
       }
   }
 
 return $text_lines;
 }

# create_loop_line
#   Returns new loop line created from list of tags and direct and indirect name-value hashes.
#
#   Parameters:
#	$tag_list - array of tags in the line.
#	$value_hash - hash of direct name-value pairs.
#	$tag_converter - hash converter of tags to usable names.
#	$indirect_value_hash - hash of indirect name-value pairs.
#
sub create_loop_line
  {
  my $tag_list = shift @_;
  my $value_hash = shift @_;
  my $tag_converter = shift @_;
  my $indirect_value_hash = shift @_;

  my $line = "";

  foreach my $tag (@$tag_list)
    { 
    if (exists $$value_hash{$tag})
      { $line .= "	" . $$value_hash{$tag}; }
    elsif (exists $$tag_converter{$tag})
      {
      if (exists $$indirect_value_hash{$$tag_converter{$tag}} && ($$indirect_value_hash{$$tag_converter{$tag}} ne ""))
	{ $line .= "	" . $$indirect_value_hash{$$tag_converter{$tag}}; }
      else
	{ $line .= "	."; }
      }
    elsif (exists $$indirect_value_hash{"extra_tags"} && exists $$indirect_value_hash{"extra_tags"}{$tag} && ($$indirect_value_hash{"extra_tags"}{$tag} ne ""))
      { $line .= "	" . $$indirect_value_hash{"extra_tags"}{$tag}; }
    else
      { $line .= "	."; }
    }

  return $line;
  }


# create_chemical_shift_reference_frame
#   Returns an array of text_lines given the description of the save_frame and the bmrb hash structure.
#
#   Parameters:
#	$save_frame - ref to save_frame hash structure.
#	$bmrb_hlist - ref to bmrb hash structure.
#
sub create_chemical_shift_reference_frame
 {
 my $save_frame = shift @_;
 my $bmrb_hlist = shift @_;

 my $text_lines = [];

 foreach my $section (@{$$save_frame{"sections"}})
   {
   if ($$section{"type"} eq "text")
     { push @$text_lines, (@{$$section{"value"}}); }
   elsif($$section{"type"} eq "reference_loop")
     {
     push @$text_lines, "   loop_";
     foreach my $tag (@{$$bmrb_hlist{"reference_fields"}})
       { push @$text_lines, "     " . $tag; }

     push @$text_lines, "";

     foreach my $reference (values %{$$bmrb_hlist{"reference"}})
       {
       my $ref_line = "";
       foreach my $ref_field (@$reference)
	 { 
	 if ($ref_field =~ /\s+/)
	   { $ref_line .= "	\'" . $ref_field . "\'"; }
	 else
	   { $ref_line .= "	" . $ref_field; }
	 }

       push @$text_lines, $ref_line;
       }

     push @$text_lines, "","   stop_";
     }
   }

 return $text_lines;
 }


# create_coupling_constants_frame
#   Returns an array of text_lines given the description of the save_frame and the bmrb hash structure.
#
#   Parameters:
#	$save_frame - ref to save_frame hash structure.
#	$bmrb_hlist - ref to bmrb hash structure.
#
sub create_coupling_constants_frame
 {
 my $save_frame = shift @_;
 my $bmrb_hlist = shift @_;

 my $text_lines = [];

 foreach my $section (@{$$save_frame{"sections"}})
   {
   if ($$section{"type"} eq "text")
     { push @$text_lines, (@{$$section{"value"}}); }
   elsif($$section{"type"} eq "coupling_constants")
     {
     push @$text_lines, "   loop_";
     foreach my $tag (@{$$section{"all_tags"}})
       { push @$text_lines, "     " . $tag; }

     push @$text_lines, "";

     foreach my $cc_elem (@{$$save_frame{"data"}{"cclist"}})
       {
#	   $dumper->dumpValue($cc_elem);
	   my $cc_line = "";
	   foreach my $tag (@{$$section{"all_tags"}})
	   { 
	       my $data_tag = (grep {($$coupling_constants_saveframe{$_}{"bmrbFieldName"} eq $tag)} keys %$coupling_constants_saveframe) [0]; 
	       if (exists $$cc_elem{$data_tag})
	          {$cc_line .= "     " . $$cc_elem{$data_tag};}
	       else
	          {$cc_line .= "      .";}
	   }
	   push @$text_lines, $cc_line;
       }
     push @$text_lines, "","   stop_";
     }
   }

 return $text_lines;
 }



# create_secondary_structure_frame
#   Returns an array of text_lines given the description of the save_frame and the bmrb hash structure.
#
#   Parameters:
#	$save_frame - ref to save_frame hash structure.
#	$bmrb_hlist - ref to bmrb hash structure.
#
sub create_secondary_structure_frame
 {
 my $save_frame = shift @_;
 my $bmrb_hlist = shift @_;

 my $text_lines = [];

 foreach my $section (@{$$save_frame{"sections"}})
   {
   if ($$section{"type"} eq "text")
     { push @$text_lines, (@{$$section{"value"}}); }
   elsif($$section{"type"} eq "secondary_structure")
     {
     push @$text_lines, "   loop_";
     foreach my $tag (@{$$section{"all_tags"}})
       { push @$text_lines, "     " . $tag; }

     push @$text_lines, "";

     foreach my $ss_elem (@{$$save_frame{"data"}{"sslist"}})
       {
	   my $ss_line = "";
	   foreach my $tag (@{$$section{"all_tags"}})
	   { 
	       my $data_tag = (grep {($$secondary_structure_saveframe{$_}{"bmrbFieldName"} eq $tag);} keys %$secondary_structure_saveframe) [0]; 
	       if (exists $$ss_elem{$data_tag})
	          {$ss_line .= "     " . $$ss_elem{$data_tag};}
	       else
	          {$ss_line .= "      .";}
	   }
	   push @$text_lines, $ss_line;
       }
     push @$text_lines, "","   stop_";
     }
   }

 return $text_lines;
 }

# module must return true
return 1;
