Subversion Repositories wimsdev

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     WIMSchem Elements: Chemistry molecular diagram drawing tool.
  3.    
  4.     (c) 2005 Dr. Alex M. Clark
  5.    
  6.     Released as GNUware, under the Gnu Public License (GPL)
  7.    
  8.     See www.gnu.org for details.
  9. */
  10.  
  11. package WIMSchem;
  12.  
  13. import java.util.*;
  14.  
  15. /*
  16.     Internal molecule datastructure, which captures the basic information about a molecule as envisaged by the traditional diagram
  17.     notation. The core data is stored as a list of labelled nodes (atoms) and labelled edges (bonds). Besides very basic editing and
  18.     convenient object manipulation, a few computed properties are available, some of which are cached.
  19. */
  20.  
  21. public class Molecule
  22. {
  23.     class Atom
  24.     {
  25.         String Element;
  26.         double X,Y;
  27.         int Charge,Unpaired;
  28.         int HExplicit;
  29.         int MapNum;
  30.     }
  31.     class Bond
  32.     {
  33.         int From,To;
  34.         int Order,Type;
  35.     }
  36.    
  37.     public static final int HEXPLICIT_UNKNOWN=-1;
  38.    
  39.     public static final int BONDTYPE_NORMAL=0;
  40.     public static final int BONDTYPE_INCLINED=1;
  41.     public static final int BONDTYPE_DECLINED=2;
  42.     public static final int BONDTYPE_UNKNOWN=3;
  43.    
  44.     public static final String[] ELEMENTS={null,
  45.         "H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co",
  46.         "Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I",
  47.         "Xe","Cs","Ba","La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au",
  48.         "Hg","Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa","U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No","Lr"};
  49.  
  50.     public static final double[] WEIGHTS={0.0,
  51.         1.00794,4.002602,6.941,9.01218,10.811,12.011,14.00674,15.9994,18.998403,20.1797,
  52.         22.989768,24.305,26.981539,28.0855,30.973762,32.066,35.4527,39.948,39.0983,40.078,
  53.         44.95591,47.88,50.9415,51.9961,54.93805,55.847,58.9332,58.6934,63.546,65.39,
  54.         69.723,72.61,74.92159,78.96,79.904,83.8,85.4678,87.62,88.90585,91.224,
  55.         92.90638,95.94,97.9072,101.07,102.9055,106.42,107.8682,112.411,114.818,118.71,
  56.         121.760,127.6,126.90447,131.29,132.90543,137.327,138.9055,140.115,140.90765,144.24,
  57.         144.9127,150.36,151.965,157.25,158.92534,162.50,164.93032,167.26,168.93421,173.04,
  58.         174.967,178.49,180.9479,183.84,186.207,190.23,192.22,195.08,196.96654,200.59,
  59.         204.3833,207.2,208.98037,208.9824,209.9871,222.0176,223.0197,226.0254,227.0278,232.0381,
  60.         231.03588,238.0289,237.048,244.0642,243.0614,247.0703,247.0703,251.0796,252.083,257.0951,
  61.         258.1,259.1009,262.11};                                                                                                              
  62.                                
  63.        
  64.  
  65.     boolean invalMinMax=false;
  66.     double minX=0,minY=0,maxX=0,maxY=0;
  67.     ArrayList<Atom> atoms=new ArrayList<Atom>();
  68.     ArrayList<Bond> bonds=new ArrayList<Bond>();
  69.    
  70.     // computed graph topology properties
  71.     int graph[][]=null;
  72.     int ringID[]=null,compID[]=null,priority[]=null;
  73.     ArrayList<Object> ring5=null,ring6=null,ring7=null; // common ring sizes
  74.    
  75.     // computed stereochemistry properties
  76.     public static final int STEREO_NONE=0; // topology does not allow any stereoisomers
  77.     public static final int STEREO_POS=1; // R or Z, depending on type
  78.     public static final int STEREO_NEG=2; // S or E, depending on type
  79.     public static final int STEREO_UNKNOWN=3; // topology allows stereoisomers, but wedges/geometry do not resolve which
  80.     int chiral[]=null; // (per atom)
  81.     int cistrans[]=null; // (per bond)
  82.  
  83.     // data for calculation of implicit hydrogens
  84.     static final String[] HYVALENCE_EL={"C","N","O","S","P"};
  85.     static final int[]   HYVALENCE_VAL={ 4,  3,  2,  2,  3 };
  86.    
  87.     // ------------------ public functions --------------------
  88.  
  89.     public Molecule()
  90.     {
  91.     }
  92.  
  93.  
  94.     // returns an equivalent replica of the molecule
  95.     public Molecule Clone()
  96.     {
  97.         Molecule mol=new Molecule();
  98.         for( int n=1;n<=NumAtoms();n++)
  99.         {
  100.             Atom a=atoms.get(n-1);
  101.             int num=mol.AddAtom(a.Element,a.X,a.Y,a.Charge,a.Unpaired);
  102.             mol.SetAtomHExplicit(num,AtomHExplicit(n));
  103.             mol.SetAtomMapNum(num,AtomMapNum(n));
  104.         }
  105.         for( int n=1;n<=NumBonds();n++)
  106.         {
  107.             Bond b=bonds.get(n-1);
  108.             mol.AddBond(b.From,b.To,b.Order,b.Type);
  109.         }
  110.         return mol;
  111.     }
  112.  
  113.    
  114.     // ----- fetching information directly from the datastructures ----
  115.        
  116.     public int NumAtoms() {return atoms.size();}
  117.     public int NumBonds() {return bonds.size();}
  118.    
  119.     public String AtomElement(int N) {return atoms.get(N-1).Element;}
  120.     public double AtomX(int N) {return atoms.get(N-1).X;}
  121.     public double AtomY(int N) {return atoms.get(N-1).Y;}
  122.     public int AtomCharge(int N) {return atoms.get(N-1).Charge;}
  123.     public int AtomUnpaired(int N) {return atoms.get(N-1).Unpaired;}
  124.     public int AtomHExplicit(int N) {return atoms.get(N-1).HExplicit;}
  125.     public int AtomMapNum(int N) {return atoms.get(N-1).MapNum;}
  126.    
  127.     public int BondFrom(int N) {return bonds.get(N-1).From;}
  128.     public int BondTo(int N) {return bonds.get(N-1).To;}
  129.     public int BondOrder(int N) {return bonds.get(N-1).Order;}
  130.     public int BondType(int N) {return bonds.get(N-1).Type;}
  131.    
  132.     // ----- modifying information in the datastructures ----
  133.  
  134.     // adds an atom to the end of the molecule; it is fairly reasonable to assume that the new one will be at position==NumAtoms(),
  135.     // and that all other atoms will be in the same places, but the position is returned for convenience
  136.     public int AddAtom(String Element,double X,double Y) {return AddAtom(Element,X,Y,0,0);}
  137.     public int AddAtom(String Element,double X,double Y,int Charge,int Unpaired)
  138.     {
  139.         if( X<minX || NumAtoms()==0) minX=X;
  140.         if( X>maxX || NumAtoms()==0) maxX=X;
  141.         if( Y<minY || NumAtoms()==0) minY=Y;
  142.         if( Y>maxY || NumAtoms()==0) maxY=Y;
  143.        
  144.         Atom a=new Atom();
  145.         a.Element=Element;
  146.         a.X=X;
  147.         a.Y=Y;
  148.         a.Charge=Charge;
  149.         a.Unpaired=Unpaired;
  150.         a.HExplicit=HEXPLICIT_UNKNOWN;
  151.         atoms.add(a);
  152.         TrashGraph();
  153.         return atoms.size();
  154.     }
  155.     public void SetAtomElement(int N,String Element) {atoms.get(N-1).Element=Element; TrashPriority();}
  156.     public void SetAtomPos(int N,double X,double Y)
  157.     {
  158.         atoms.get(N-1).X=X;
  159.         atoms.get(N-1).Y=Y;
  160.         invalMinMax=true;
  161.         TrashStereo();
  162.     }
  163.     public void SetAtomCharge(int N,int Charge) {atoms.get(N-1).Charge=Charge; TrashPriority();}
  164.     public void SetAtomUnpaired(int N,int Unpaired) {atoms.get(N-1).Unpaired=Unpaired; TrashPriority();}
  165.     public void SetAtomHExplicit(int N,int HExplicit) {atoms.get(N-1).HExplicit=HExplicit; TrashPriority();}
  166.     public void SetAtomMapNum(int N,int MapNum) {atoms.get(N-1).MapNum=MapNum;}
  167.    
  168.     // adds an atom the end of the molecule; the position of the new bond is returned for convenience
  169.     public int AddBond(int From,int To,int Order) {return AddBond(From,To,Order,BONDTYPE_NORMAL);}
  170.     public int AddBond(int From,int To,int Order,int Type)
  171.     {
  172.         Bond b=new Bond();
  173.         b.From=From;
  174.         b.To=To;
  175.         b.Order=Order;
  176.         b.Type=Type;
  177.         bonds.add(b);
  178.         TrashGraph();
  179.         return bonds.size();
  180.     }
  181.    
  182.     public void SetBondFromTo(int N,int From,int To)
  183.     {
  184.         bonds.get(N-1).From=From;
  185.         bonds.get(N-1).To=To;
  186.         TrashGraph();
  187.     }
  188.     public void SetBondOrder(int N,int Order) {bonds.get(N-1).Order=Order; TrashPriority();}
  189.     public void SetBondType(int N,int Type) {bonds.get(N-1).Type=Type; TrashStereo();}
  190.    
  191.     // deletes the indicated atom, and also any bonds which were connected to it
  192.     public void DeleteAtomAndBonds(int N)
  193.     {
  194.         for( int n=0;n<NumBonds();)
  195.         {
  196.             Bond b=bonds.get(n);
  197.             if( b.From==N || b.To==N) bonds.remove(n);
  198.             else
  199.             {
  200.                 if( b.From>N) b.From--;
  201.                 if( b.To>N) b.To--;
  202.                 n++;
  203.             }
  204.         }
  205.         atoms.remove(N-1);
  206.         invalMinMax=true;
  207.         TrashGraph();
  208.     }
  209.     public void DeleteBond(int N)
  210.     {
  211.         bonds.remove(N-1);
  212.         TrashGraph();
  213.     }
  214.      
  215.     // ----- fetching information which is computed from the underlying data, usually cached -----
  216.      
  217.     public double MinX() {if( invalMinMax) DetermineMinMax(); return minX;}
  218.     public double MaxX() {if( invalMinMax) DetermineMinMax(); return maxX;}
  219.     public double MinY() {if( invalMinMax) DetermineMinMax(); return minY;}
  220.     public double MaxY() {if( invalMinMax) DetermineMinMax(); return maxY;}
  221.     public double RangeX() {return maxX-minX;}
  222.     public double RangeY() {return maxY-minY;}
  223.    
  224.     // return the index of the bond, if any, which connects the two indicated atoms
  225.     public int FindBond(int A1,int A2)
  226.     {
  227.         for( int n=1;n<=NumBonds();n++)
  228.         {
  229.             if( BondFrom(n)==A1 && BondTo(n)==A2) return n;
  230.             if( BondTo(n)==A1 && BondFrom(n)==A2) return n;
  231.         }
  232.         return 0;
  233.     }
  234.  
  235.     // for a bond, returns the end which is not==Ref; return value will be From,To or 0    
  236.     public int BondOther(int N,int Ref)
  237.     {
  238.         if( BondFrom(N)==Ref) return BondTo(N);
  239.         if( BondTo(N)==Ref) return BondFrom(N);
  240.         return 0;
  241.     }
  242.  
  243.     // returns whether an atom logically ought to be drawn "explicitly";if false, it is a carbon which should be just a dot
  244.     public boolean AtomExplicit(int N)
  245.     {
  246.         if( atoms.get(N-1).Element.compareTo("C")!=0 || atoms.get(N-1).Charge!=0 || atoms.get(N-1).Unpaired!=0) return true;
  247.         for( int n=0;n<bonds.size();n++) if( bonds.get(n).From==N || bonds.get(n).To==N) return false;
  248.         return true;
  249.     }
  250.    
  251.     // uses either explicit or computed number to determine how many hydrogens the atom has; the field for explicit hydrogens takes
  252.     // absolute preference, if it has its default value of 'unknown', the number is computed by looking up the hydrogen capacity for
  253.     // the element (most of which are zero), subtracting the total of bond orders, then returning the difference, or zero; the calculation
  254.     // tends to err on the side of too few, since the concept is just an aid to drawing organic structures, not a cheminformatic attempt
  255.     // to compensate for 2 1/2 decades of bad file formats
  256.     // (note: returns "implicit"+"explicit", but does NOT count "actual" hydrogens, i.e. those which have their own atom nodes)
  257.     public int AtomHydrogens(int N)
  258.     {
  259.         int hy=AtomHExplicit(N);
  260.         if( hy!=HEXPLICIT_UNKNOWN) return hy;
  261.         for( int n=0;n<HYVALENCE_EL.length;n++) if( HYVALENCE_EL[n].compareTo(AtomElement(N))==0) {hy=HYVALENCE_VAL[n]; break;}
  262.         if( hy==HEXPLICIT_UNKNOWN) return 0;
  263.         int ch=AtomCharge(N);
  264.         if( AtomElement(N).compareTo("C")==0) ch=-Math.abs(ch);
  265.         hy+=ch-AtomUnpaired(N);
  266.         for( int n=1;n<=NumBonds();n++) if( BondFrom(n)==N || BondTo(n)==N) hy-=BondOrder(n);
  267.         return hy<0 ? 0 : hy;
  268.     }
  269.      
  270.     // returns the numerical ID of the ring block in which the atom resides, or 0 if it is not in a ring  
  271.     public int AtomRingBlock(int N)
  272.     {
  273.         if( graph==null) BuildGraph();
  274.         if( ringID==null) BuildRingID();
  275.         return ringID[N-1];
  276.     }
  277.     // returns whether or not a bond resides in a ring (i.e. ring block of each end is the same and nonzero)
  278.     public boolean BondInRing(int N)
  279.     {
  280.         int r1=AtomRingBlock(BondFrom(N)),r2=AtomRingBlock(BondTo(N));
  281.         return r1>0 && r1==r2;
  282.     }
  283.    
  284.     // returns the connected component ID of the atom, always 1 or more
  285.     public int AtomConnComp(int N)
  286.     {
  287.         if( graph==null) BuildGraph();
  288.         if( compID==null) BuildConnComp();
  289.         return compID[N-1];
  290.     }
  291.    
  292.     // returns the number of neighbours of an atom
  293.     public int AtomAdjCount(int N)
  294.     {
  295.         if( graph==null) BuildGraph();
  296.         return graph[N-1].length;
  297.     }
  298.    
  299.     // returns the actual adjacency list of an atom; unfortunately this has to make a clone of the array, since the numbers are converted
  300.     // to 1-based indices, and we would not want callers modifying it anyway
  301.     public int[] AtomAdjList(int N)
  302.     {
  303.         if( graph==null) BuildGraph();
  304.         int[] adj=graph[N-1].clone();
  305.         for( int n=0;n<adj.length;n++) adj[n]++;
  306.         return adj;
  307.     }
  308.    
  309.     // returns atom-based priority according to the Cahn-Ingold-Prelog rules
  310.     public int AtomPriority(int N)
  311.     {
  312.         if( graph==null) BuildGraph();
  313.         if( compID==null) BuildConnComp();
  314.         if( priority==null) BuildPriority();
  315.         return priority[N-1];
  316.     }
  317.    
  318.     // return the tetrahedral chirality of the given atom
  319.     public int AtomChirality(int N)
  320.     {
  321.         if( graph==null) BuildGraph();
  322.         if( compID==null) BuildConnComp();
  323.         if( priority==null) BuildPriority();
  324.         if( chiral==null) BuildChirality();
  325.         return chiral[N-1];
  326.     }
  327.    
  328.     // return the cis/trans style stereochemistry of the given bond
  329.     public int BondStereo(int N)
  330.     {
  331.         if( graph==null) BuildGraph();
  332.         if( compID==null) BuildConnComp();
  333.         if( priority==null) BuildPriority();
  334.         if( cistrans==null) BuildCisTrans();
  335.         return cistrans[N-1];
  336.     }
  337.    
  338.     // returns _all_ rings of indicated size; each item in the array list is an array of int[Size], a consecutively ordered array of atom
  339.     // numbers; uses a recursive depth first search, which must be bounded above by Size being small in order to avoid exponential blowup
  340.     public int[][] FindRingSize(int Size)
  341.     {
  342.         ArrayList<Object> rings=null;
  343.         if( Size==5 && ring5!=null) rings=ring5;
  344.         if( Size==6 && ring6!=null) rings=ring6;
  345.         if( Size==7 && ring7!=null) rings=ring7;
  346.    
  347.         if( rings==null)
  348.         {
  349.             if( graph==null) BuildGraph();
  350.             if( ringID==null) BuildRingID();
  351.  
  352.             rings=new ArrayList<Object>();
  353.             for( int n=1;n<=NumAtoms();n++) if( ringID[n-1]>0)
  354.             {
  355.                 int path[]=new int[Size];
  356.                 path[0]=n;
  357.                 RecursiveRingFind(path,1,Size,ringID[n-1],rings);
  358.             }
  359.  
  360.             if( Size==5) ring5=rings;
  361.             if( Size==6) ring6=rings;
  362.             if( Size==7) ring7=rings;
  363.         }
  364.        
  365.         int ret[][]=new int[rings.size()][];
  366.         for( int n=0;n<rings.size();n++) ret[n]=((int[])rings.get(n)).clone();
  367.         return ret;
  368.     }
  369.    
  370.     // compare to another molecule instance; null is equivalent to empty; return values:
  371.     //     -1 if Mol < Other
  372.     //      0 if Mol == Other
  373.     //      1 if Mol > Other
  374.     //  can be used to sort molecules, albeit crudely; does not do any kind of canonical preprocessing
  375.     public int CompareTo(Molecule Other)
  376.     {
  377.         if( Other==null) return NumAtoms()==0 ? 0 : 1; // null is equivalent to empty
  378.         if( NumAtoms()<Other.NumAtoms()) return -1;
  379.         if( NumAtoms()>Other.NumAtoms()) return 1;
  380.         if( NumBonds()<Other.NumBonds()) return -1;
  381.         if( NumBonds()>Other.NumBonds()) return 1;
  382.        
  383.         for( int n=1;n<=NumAtoms();n++)
  384.         {
  385.             int c=AtomElement(n).compareTo(Other.AtomElement(n));
  386.             if( c!=0) return c;
  387.             if( AtomX(n)<Other.AtomX(n)) return -1; if( AtomX(n)>Other.AtomX(n)) return 1;
  388.             if( AtomY(n)<Other.AtomY(n)) return -1; if( AtomY(n)>Other.AtomY(n)) return 1;
  389.             if( AtomCharge(n)<Other.AtomCharge(n)) return -1; if( AtomCharge(n)>Other.AtomCharge(n)) return 1;
  390.             if( AtomUnpaired(n)<Other.AtomUnpaired(n)) return -1; if( AtomUnpaired(n)>Other.AtomUnpaired(n)) return 1;
  391.             if( AtomHExplicit(n)<Other.AtomHExplicit(n)) return -1; if( AtomHExplicit(n)>Other.AtomHExplicit(n)) return 1;
  392.         }
  393.         for( int n=1;n<=NumBonds();n++)
  394.         {
  395.             if( BondFrom(n)<Other.BondFrom(n)) return -1; if( BondFrom(n)>Other.BondFrom(n)) return 1;
  396.             if( BondTo(n)<Other.BondTo(n)) return -1; if( BondTo(n)>Other.BondTo(n)) return 1;
  397.             if( BondOrder(n)<Other.BondOrder(n)) return -1; if( BondOrder(n)>Other.BondOrder(n)) return 1;
  398.             if( BondType(n)<Other.BondType(n)) return -1; if( BondType(n)>Other.BondType(n)) return 1;
  399.         }
  400.        
  401.         return 0;
  402.     }
  403.  
  404.     // lookup the atomic number for the element, or return 0 if not in the periodic table
  405.     int AtomicNumber(int N) {return AtomicNumber(AtomElement(N));}
  406.     int AtomicNumber(String El)
  407.     {
  408.         for( int n=1;n<ELEMENTS.length;n++) if( ELEMENTS[n].compareTo(El)==0) return n;
  409.         return 0;
  410.     }
  411.  
  412.     // takes the indicated molecular fragment and appends it to the end of the current molecule, with order preserved
  413.     public void Append(Molecule Frag)
  414.     {
  415.         int base=NumAtoms();
  416.         for( int n=1;n<=Frag.NumAtoms();n++)
  417.         {
  418.             int num=AddAtom(Frag.AtomElement(n),Frag.AtomX(n),Frag.AtomY(n),Frag.AtomCharge(n),Frag.AtomUnpaired(n));
  419.             SetAtomHExplicit(num,Frag.AtomHExplicit(n));
  420.             SetAtomMapNum(num,Frag.AtomMapNum(n));
  421.         }
  422.         for( int n=1;n<=Frag.NumBonds();n++)
  423.         {
  424.             AddBond(Frag.BondFrom(n)+base,Frag.BondTo(n)+base,Frag.BondOrder(n),Frag.BondType(n));
  425.         }
  426.     }
  427.  
  428.     // returns a molecule which is smaller than the current one, according to the mask (which must be of size #atoms); boundary cases
  429.     // are a null molecule or cloned copy
  430.     public Molecule Subgraph(boolean[] Mask)
  431.     {
  432.         int[] invidx=new int[NumAtoms()];
  433.         int sum=0;
  434.         for( int n=0;n<NumAtoms();n++) if( Mask[n]) invidx[n]=++sum; else invidx[n]=0;
  435.         if( sum==0) return new Molecule();
  436.         if( sum==NumAtoms()) return Clone();
  437.        
  438.         Molecule frag=new Molecule();
  439.         for( int n=1;n<=NumAtoms();n++) if( Mask[n-1])
  440.         {
  441.             int num=frag.AddAtom(AtomElement(n),AtomX(n),AtomY(n),AtomCharge(n),AtomUnpaired(n));
  442.             frag.SetAtomHExplicit(num,AtomHExplicit(n));
  443.             frag.SetAtomMapNum(num,AtomMapNum(n));
  444.         }
  445.         for( int n=1;n<=NumBonds();n++)
  446.         {
  447.             int from=invidx[BondFrom(n)-1],to=invidx[BondTo(n)-1];
  448.             if( from>0 && to>0) frag.AddBond(from,to,BondOrder(n),BondType(n));
  449.         }
  450.        
  451.         return frag;
  452.     }
  453.  
  454.    
  455.     // convenient debugging mechanism
  456.     public void Dump()
  457.     {
  458.         System.out.println("#Atoms="+NumAtoms()+" #Bonds="+NumBonds());
  459.         for( int n=0;n<NumAtoms();n++)
  460.         {
  461.             Atom a=atoms.get(n);
  462.             System.out.println(" A"+(n+1)+": "+a.Element+"="+a.X+","+a.Y+";"+a.Charge+","+a.Unpaired);
  463.         }
  464.         for( int n=0;n<NumBonds();n++)
  465.         {
  466.             Bond b=bonds.get(n);
  467.             System.out.println(" B"+(n+1)+": "+b.From+"-"+b.To+"="+b.Order+","+b.Type);
  468.         }
  469.     }
  470.  
  471.     // ------------------ private functions --------------------
  472.    
  473.     // for when the connectivity changes somehow; abandon graph properties
  474.     void TrashGraph()
  475.     {
  476.         graph=null;
  477.         ringID=null;
  478.         ring5=null;
  479.         ring6=null;
  480.         ring7=null;
  481.         compID=null;
  482.         TrashPriority();
  483.     }
  484.     void TrashPriority()
  485.     {
  486.         priority=null;
  487.         TrashStereo();
  488.     }
  489.     void TrashStereo()
  490.     {
  491.         chiral=null;
  492.         cistrans=null;
  493.     }
  494.    
  495.     // update the cache of atom range
  496.     void DetermineMinMax()
  497.     {
  498.         invalMinMax=false;
  499.         if( NumAtoms()==0)
  500.         {
  501.             minX=maxX=minY=maxY=0;
  502.             return;
  503.         }
  504.         minX=maxX=AtomX(1);
  505.         minY=maxY=AtomY(1);
  506.         for( int n=2;n<=NumAtoms();n++)
  507.         {
  508.             double x=AtomX(n),y=AtomY(n);
  509.             minX=Math.min(minX,x);
  510.             maxX=Math.max(maxX,x);
  511.             minY=Math.min(minY,y);
  512.             maxY=Math.max(maxY,y);
  513.         }
  514.     }
  515.  
  516.     // update the cache of the molecular graph, in adjacency format, rather than the edge list format; the former is much better for
  517.     // graph algorithms, the latter much more suited to capturing sketcher-ish information content; note that the graph indices are
  518.     // zero-based
  519.     void BuildGraph()
  520.     {
  521.         graph=new int[NumAtoms()][];
  522.         for( int n=1;n<=NumBonds();n++)
  523.         {
  524.             int bf=BondFrom(n)-1,bt=BondTo(n)-1;
  525.             if( bf==bt) continue; // ouch!
  526.             int lf=graph[bf]==null ? 0 : graph[bf].length,lt=graph[bt]==null ? 0 : graph[bt].length;
  527.             int bl[]=new int[lf+1];
  528.             for( int i=0;i<lf;i++) bl[i]=graph[bf][i];
  529.             bl[lf]=bt;
  530.             graph[bf]=bl;
  531.             bl=new int[lt+1];
  532.             for( int i=0;i<lt;i++) bl[i]=graph[bt][i];
  533.             bl[lt]=bf;
  534.             graph[bt]=bl;
  535.         }
  536.         for( int n=0;n<NumAtoms();n++) if( graph[n]==null) graph[n]=new int[0];
  537.        
  538.         /*for( int n=0;n<NumAtoms();n++)
  539.         {
  540.             System.out.print("#"+n+":");
  541.             for( int i=0;i<graph[n].length;i++) System.out.print(" "+graph[n][i]);
  542.             System.out.println();
  543.         }*/
  544.     }
  545.    
  546.     // passes over the graph establishing which component each atom belongs to
  547.     void BuildConnComp()
  548.     {
  549.         compID=new int[NumAtoms()];
  550.         for( int n=0;n<NumAtoms();n++) compID[n]=0;
  551.         int comp=1;
  552.         compID[0]=comp;
  553.  
  554.         // (not very efficient, should use a stack-based depth first search)
  555.         while (true)
  556.         {
  557.             boolean anything=false;
  558.             for( int n=0;n<NumAtoms();n++) if( compID[n]>0)
  559.             {
  560.                 for( int i=0;i<graph[n].length;i++) if( compID[graph[n][i]]==0)
  561.                 {
  562.                     compID[graph[n][i]]=comp;
  563.                     anything=true;
  564.                 }
  565.             }
  566.            
  567.             if( !anything)
  568.             {
  569.                 for( int n=0;n<NumAtoms();n++) if( compID[n]==0) {compID[n]=++comp; anything=true; break;}
  570.                 if( !anything) break;
  571.             }
  572.         }
  573.     }
  574.  
  575.     // generates Cahn-Ingold-Prelog priority for each atom, where degeneracies are indicated by having the same number
  576.     void BuildPriority()
  577.     {
  578.         // build a graph representation which has entries replicated according to bond order
  579.         int cipgr[][]=new int[NumAtoms()][];
  580.         for( int n=0;n<NumAtoms();n++) if( cipgr[n]==null)
  581.         {
  582.             cipgr[n]=new int[AtomHydrogens(n+1)];
  583.             for( int i=0;i<cipgr[n].length;i++) cipgr[n][i]=-1;
  584.         }
  585.         for( int n=1;n<=NumBonds();n++)
  586.         {
  587.             int bf=BondFrom(n)-1,bt=BondTo(n)-1,bo=BondOrder(n);
  588.             if( bf==bt || bo==0) continue;
  589.             int lf=cipgr[bf].length,lt=cipgr[bt].length;
  590.             int bl[]=new int[lf+bo];
  591.             for( int i=0;i<lf;i++) bl[i]=cipgr[bf][i];
  592.             for( int i=0;i<bo;i++) bl[lf++]=bt;
  593.             cipgr[bf]=bl;
  594.             bl=new int[lt+bo];
  595.             for( int i=0;i<lt;i++) bl[i]=cipgr[bt][i];
  596.             for( int i=0;i<bo;i++) bl[lt++]=bf;
  597.             cipgr[bt]=bl;
  598.         }
  599.  
  600.         // seed the priorities with atomic number
  601.         priority=new int[NumAtoms()];
  602.         boolean anyActualH=false;
  603.         for( int n=0;n<NumAtoms();n++) {priority[n]=AtomicNumber(n+1); if( priority[n]==1) anyActualH=true;}
  604.        
  605.         // pass through and reassign priorities as many times as necessary, until no change
  606.         int prigr[][]=new int[NumAtoms()][];
  607.         while (true)
  608.         {
  609.             // make an equivalent to cipgr which has priorities instead of indices
  610.             for( int n=0;n<NumAtoms();n++)
  611.             {
  612.                 prigr[n]=new int[cipgr[n].length];
  613.                 for( int i=0;i<prigr[n].length;i++) if( cipgr[n][i]<0) prigr[n][i]=1; else prigr[n][i]=priority[cipgr[n][i]];
  614.                 int p=0;
  615.                 while (p<prigr[n].length-1)
  616.                 {
  617.                     if( prigr[n][p]<prigr[n][p+1]) {
  618.                         int i=prigr[n][p]; prigr[n][p]=prigr[n][p+1]; prigr[n][p+1]=i;
  619.                         if( p>0) p--;
  620.                     } else p++;
  621.                 }
  622.             }
  623.            
  624.             // divide each priority category into groups, then for each of these groups, split the contents out and reassign
  625.             int groups[][]=SortAndGroup(priority);
  626.             int nextpri=anyActualH ? 0 : 1;
  627.             boolean repartitioned=false;
  628.            
  629.             for( int n=0;n<groups.length;n++)
  630.             {
  631.                 // sort the groups according to their cipgr contents
  632.                 int p=0;
  633.                 while (p<groups[n].length-1)
  634.                 {
  635.                     int i1=groups[n][p],i2=groups[n][p+1];
  636.                     int cmp=0,sz=Math.max(prigr[i1].length,prigr[i2].length);
  637.                     for( int i=0;i<sz;i++)
  638.                     {
  639.                         int v1=i<prigr[i1].length ? prigr[i1][i] : 0,v2=i<prigr[i2].length ? prigr[i2][i] : 0;
  640.                         if( v1<v2) {cmp=-1; break;}
  641.                         if( v1>v2) {cmp=1; break;}
  642.                     }
  643.                     if( cmp>0) {groups[n][p]=i2; groups[n][p+1]=i1; if( p>0) p--;}
  644.                     else p++;
  645.                 }
  646.                
  647.                 for( int i=0;i<groups[n].length;i++)
  648.                 {
  649.                     if( i==0) nextpri++;
  650.                     else if( prigr[groups[n][i]].length!=prigr[groups[n][i-1]].length) {nextpri++; repartitioned=true;}
  651.                     else
  652.                     {
  653.                         for( int j=0;j<prigr[groups[n][i]].length;j++)
  654.                             if( prigr[groups[n][i]][j]!=prigr[groups[n][i-1]][j]) {nextpri++; repartitioned=true; break;}
  655.                     }
  656.                    
  657.                     priority[groups[n][i]]=nextpri;
  658.                 }
  659.             }
  660.  
  661.             if( !repartitioned) break;
  662.         }
  663.     }
  664.  
  665.     // compute the chirality values for each atom centre
  666.     void BuildChirality()
  667.     {
  668.         chiral=new int[NumAtoms()];
  669.        
  670.         boolean haswedge[]=new boolean[NumAtoms()];
  671.         for( int n=0;n<NumAtoms();n++) haswedge[n]=false;
  672.         for( int n=1;n<=NumBonds();n++) if( BondType(n)==BONDTYPE_INCLINED || BondType(n)==BONDTYPE_DECLINED) haswedge[BondFrom(n)-1]=true;
  673.        
  674.         int pri[]=new int[4];
  675.         double x[]=new double[4],y[]=new double[4],z[]=new double[4];
  676.        
  677.         for( int n=0;n<NumAtoms();n++)
  678.         {
  679.             chiral[n]=STEREO_NONE;
  680.             if( !(graph[n].length==4 || (graph[n].length==3 && AtomHydrogens(n+1)==1))) continue;
  681.  
  682.             for( int i=0;i<graph[n].length;i++)
  683.             {
  684.                 pri[i]=priority[graph[n][i]];
  685.                 x[i]=AtomX(graph[n][i]+1)-AtomX(n+1);
  686.                 y[i]=AtomY(graph[n][i]+1)-AtomY(n+1);
  687.                 z[i]=0;
  688.                 if( haswedge[n])
  689.                     for( int j=1;j<=NumBonds();j++) if( BondFrom(j)==n+1 && BondTo(j)==graph[n][i]+1)
  690.                 {
  691.                     if( BondType(j)==BONDTYPE_INCLINED) z[i]=1;
  692.                     if( BondType(j)==BONDTYPE_DECLINED) z[i]=-1;
  693.                     break;
  694.                 }
  695.             }
  696.             if( graph[n].length==3) {pri[3]=0; x[3]=0; y[3]=0; z[3]=0;}
  697.            
  698.             int p=0;
  699.             while (p<3)
  700.             {
  701.                 if( pri[p]>pri[p+1])
  702.                 {
  703.                     int i; double d;
  704.                     i=pri[p]; pri[p]=pri[p+1]; pri[p+1]=i;
  705.                     d=x[p]; x[p]=x[p+1]; x[p+1]=d;
  706.                     d=y[p]; y[p]=y[p+1]; y[p+1]=d;
  707.                     d=z[p]; z[p]=z[p+1]; z[p+1]=d;
  708.                     if( p>0) p--;
  709.                 }
  710.                 else p++;
  711.             }
  712.             if( (pri[0]==0 && pri[1]==1) || pri[0]==pri[1] || pri[1]==pri[2] || pri[2]==pri[3]) continue; // no topological chirality
  713.            
  714.             chiral[n]=STEREO_UNKNOWN;
  715.             if( z[0]==0 && z[1]==0 && z[2]==0 && z[3]==0) continue; // all atoms are in-plane
  716.            
  717.             boolean sane=true;
  718.             for( int i=0;i<4;i++) if( pri[0]!=0)
  719.             {
  720.                 double r=x[i]*x[i]+y[i]*y[i]+z[i]*z[i];
  721.                 if( r<0.01*0.01) {sane=false; break;}
  722.                 r=1/Math.sqrt(r);
  723.                 x[i]=x[i]*r; y[i]=y[i]*r; z[i]=z[i]*r;
  724.             }
  725.             if( !sane) continue;
  726.            
  727.             if( pri[0]==0) // build a position for the implicit H
  728.             {
  729.                 x[0]=-(x[1]+x[2]+x[3]);
  730.                 y[0]=-(y[1]+y[2]+y[3]);
  731.                 z[0]=-(z[1]+z[2]+z[3]);
  732.                 double r=x[0]*x[0]+y[0]*y[0]+z[0]*z[0];
  733.                 if( r<0.01*0.01) {sane=false; break;}
  734.                 r=1/Math.sqrt(r);
  735.                 x[0]=x[0]*r; y[0]=y[0]*r; z[0]=z[0]*r;
  736.             }
  737.             if( !sane) continue;
  738.            
  739.             double R=0,S=0;
  740.             for( int i=1;i<=6;i++)
  741.             {
  742.                 int a=0,b=0;
  743.                 if      (i==1) {a=1; b=2;} else if( i==2) {a=2; b=3;} else if( i==3) {a=3; b=1;}
  744.                 else if( i==4) {a=2; b=1;} else if( i==5) {a=3; b=2;} else if( i==6) {a=1; b=3;}
  745.                 double xx=y[a]*z[b]-y[b]*z[a] - x[0];
  746.                 double yy=z[a]*x[b]-z[b]*x[a] - y[0];
  747.                 double zz=x[a]*y[b]-x[b]*y[a] - z[0];
  748.                 if( i<=3) R+=xx*xx+yy*yy+zz*zz; else S+=xx*xx+yy*yy+zz*zz;
  749.             }
  750.             chiral[n]=R>S ? STEREO_POS : STEREO_NEG;
  751.         }
  752.     }
  753.    
  754.     // computer the cis/trans stereochemistry for each bond
  755.     void BuildCisTrans()
  756.     {
  757.         cistrans=new int[NumBonds()];
  758.         int sf[]=new int[2],st[]=new int[2];
  759.        
  760.         for( int n=0;n<NumBonds();n++)
  761.         {
  762.             cistrans[n]=STEREO_NONE;
  763.             int bf=BondFrom(n+1)-1,bt=BondTo(n+1)-1;
  764.             if( BondOrder(n+1)!=2 || graph[bf].length<=1 || graph[bt].length<= 1 || graph[bf].length>3 || graph[bt].length>3) continue;
  765.  
  766.             int nf=0,nt=0;
  767.             for( int i=0;i<graph[bf].length;i++) if( graph[bf][i]!=bt) sf[nf++]=graph[bf][i];
  768.             for( int i=0;i<graph[bt].length;i++) if( graph[bt][i]!=bf) st[nt++]=graph[bt][i];
  769.  
  770.             if( nf==1) {if( AtomHydrogens(bf+1)!=1 || priority[sf[0]]==1) continue;}
  771.             else
  772.             {
  773.                 if( priority[sf[0]]==priority[sf[1]]) continue;
  774.                 if( priority[sf[0]]<priority[sf[1]]) {int i=sf[0]; sf[0]=sf[1]; sf[1]=i;}
  775.             }
  776.            
  777.             if( nt==1) {if( AtomHydrogens(bt+1)!=1 || priority[st[0]]==1) continue;}
  778.             else
  779.             {
  780.                 if( priority[st[0]]==priority[st[1]]) continue;
  781.                 if( priority[st[0]]<priority[st[1]]) {int i=st[0]; st[0]=st[1]; st[1]=i;}
  782.             }
  783.        
  784.             cistrans[n]=STEREO_UNKNOWN;
  785.  
  786.             double xa=AtomX(bf+1),ya=AtomY(bf+1),xb=AtomX(bt+1),yb=AtomY(bt+1);
  787.             double tha0=Math.atan2(yb-ya,xb-xa),thb0=Math.PI+tha0;
  788.             double tha1=Math.atan2(AtomY(sf[0]+1)-ya,AtomX(sf[0]+1)-xa);
  789.             double tha2=nf==2 ? Math.atan2(AtomY(sf[1]+1)-ya,AtomX(sf[1]+1)-xa) : ThetaObtuse(tha0,tha1);
  790.             double thb1=Math.atan2(AtomY(st[0]+1)-yb,AtomX(st[0]+1)-xb);
  791.             double thb2=nt==2 ? Math.atan2(AtomY(st[1]+1)-yb,AtomX(st[1]+1)-xb) : ThetaObtuse(thb0,thb1);
  792.             tha1-=tha0; tha2-=tha0; thb1-=thb0; thb2-=thb0;
  793.             tha1+=(tha1<-Math.PI ? 2*Math.PI : 0)+(tha1>Math.PI ? -2*Math.PI : 0);
  794.             tha2+=(tha2<-Math.PI ? 2*Math.PI : 0)+(tha2>Math.PI ? -2*Math.PI : 0);
  795.             thb1+=(thb1<-Math.PI ? 2*Math.PI : 0)+(thb1>Math.PI ? -2*Math.PI : 0);
  796.             thb2+=(thb2<-Math.PI ? 2*Math.PI : 0)+(thb2>Math.PI ? -2*Math.PI : 0);
  797.  
  798.             final double SMALL=5*Math.PI/180; // basically colinear
  799.             if( Math.abs(tha1)<SMALL || Math.abs(tha2)<SMALL || Math.abs(thb1)<SMALL || Math.abs(thb2)<SMALL) continue;
  800.             if( Math.abs(tha1)>Math.PI-SMALL || Math.abs(tha2)>Math.PI-SMALL) continue;
  801.             if( Math.abs(thb1)>Math.PI-SMALL || Math.abs(thb2)>Math.PI-SMALL) continue;
  802.             tha1=Math.signum(tha1); tha2=Math.signum(tha2); thb1=Math.signum(thb1); thb2=Math.signum(thb2);
  803.             if( tha1==tha2 || thb1==thb2) continue;
  804.             if( tha1*thb1<0) cistrans[n]=STEREO_POS;
  805.             if( tha1*thb1>0) cistrans[n]=STEREO_NEG;
  806.         }
  807.     }
  808.  
  809.     // generally useful function which takes a list of numbers and sorts them, then bins the unique values into sub-arrays
  810.     // (note: inefficient implementation, but could be improved easily enough)
  811.     public static int[][] SortAndGroup(int[] Val)
  812.     {
  813.         ArrayList<Integer> unique=new ArrayList<Integer>();
  814.         for( int n=0;n<Val.length;n++) unique.add(Val[n]);
  815.         Collections.sort(unique);
  816.         int p=0;
  817.         while (p<unique.size()-1)
  818.         {
  819.             if( unique.get(p)==unique.get(p+1)) unique.remove(p); else p++;
  820.         }
  821.  
  822.         int ret[][]=new int[unique.size()][];
  823.        
  824.         for( int n=0;n<Val.length;n++)
  825.         {
  826.             int grp=unique.indexOf(Val[n]);
  827.             int[] cnt=new int[ret[grp]==null ? 1 : ret[grp].length+1];
  828.             for( int i=0;i<cnt.length-1;i++) cnt[i]=ret[grp][i];
  829.             cnt[cnt.length-1]=n;
  830.             ret[grp]=cnt;
  831.         }
  832.        
  833.         return ret;
  834.     }
  835.  
  836.     // update the ring-block-identifier for each atom
  837.     void BuildRingID()
  838.     {
  839.         ringID=new int[NumAtoms()];
  840.         if( NumAtoms()==0) return;
  841.         boolean visited[]=new boolean[NumAtoms()];
  842.         for( int n=0;n<NumAtoms();n++)
  843.         {
  844.             ringID[n]=0;
  845.             visited[n]=false;
  846.         }
  847.  
  848.         int path[]=new int[NumAtoms()+1],plen=0,numVisited=0;
  849.         boolean rewound=false;
  850.         while (true)
  851.         {
  852.             int last,current;
  853.  
  854.             if( plen==0) // find an element of a new component to visit
  855.             {
  856.                 last=-1;
  857.                 for( current=0;visited[current];current++) {}
  858.             }
  859.             else
  860.             {
  861.                 last=path[plen-1];
  862.                 current=-1;
  863.                 for( int n=0;n<graph[last].length;n++) if( !visited[graph[last][n]]) {current=graph[last][n]; break;}
  864.             }
  865.            
  866.             /*System.out.print("numVisited="+numVisited+" last="+last+" current="+current+" path=");
  867.             for( int n=0;n<plen;n++) System.out.print(path[n]+" ");
  868.             System.out.println();*/
  869.            
  870.             if( current>=0 && plen>=2) // path is at least 2 items long, so look for any not-previous visited neighbours
  871.             {
  872.                 int back=path[plen-1];
  873.                 //System.out.println(" back="+back);
  874.                 for( int n=0;n<graph[current].length;n++)
  875.                 {
  876.                     int join=graph[current][n];
  877.                     //System.out.println(" join="+join+" visited[join]="+visited[join]);
  878.                     if( join!=back && visited[join])
  879.                     {
  880.                         //System.out.print(" path:");
  881.  
  882.                         path[plen]=current;
  883.                         for( int i=plen;i==plen || path[i+1]!=join;i--)
  884.                         {
  885.                             //System.out.print(" "+path[i]);
  886.  
  887.                             int id=ringID[path[i]];
  888.                             if( id==0) ringID[path[i]]=last;
  889.                             else if( id!=last)
  890.                             {
  891.                                 for( int j=0;j<NumAtoms();j++) if( ringID[j]==id) ringID[j]=last;
  892.                             }
  893.                         }
  894.                         //System.out.println();
  895.                     }
  896.                 }
  897.             }
  898.             if( current>=0)  // can mark the new one as visited
  899.             {
  900.                 visited[current]=true;
  901.                 path[plen++]=current;
  902.                 numVisited++;
  903.                 rewound=false;
  904.             }
  905.             else // otherwise, found nothing and must rewind the path
  906.             {
  907.                 plen--;
  908.                 rewound=true;
  909.             }
  910.            
  911.             if( numVisited==NumAtoms()) break;
  912.         }
  913.        
  914.         // the ring ID's are not necessarily consecutive, so reassign them to 0=none, 1..NBlocks
  915.         int nextID=0;
  916.         for( int i=0;i<NumAtoms();i++) if( ringID[i]>0)
  917.         {
  918.             nextID--;
  919.             for( int j=NumAtoms()-1;j>=i;j--) if( ringID[j]==ringID[i]) ringID[j]=nextID;
  920.         }
  921.         for( int i=0;i<NumAtoms();i++) ringID[i]=-ringID[i];
  922.     }
  923.    
  924.     // ring hunter: recursive step; finds, compares and collects
  925.     void RecursiveRingFind(int[] Path,int PSize,int Capacity,int RBlk,ArrayList<Object> Rings)
  926.     {
  927.         // not enough atoms yet, so look for new possibilities
  928.         if( PSize<Capacity)
  929.         {
  930.             int last=Path[PSize-1];
  931.             for( int n=0;n<graph[last-1].length;n++)
  932.             {
  933.                 int adj=graph[last-1][n]+1;
  934.                 if( ringID[adj-1]!=RBlk) continue;
  935.                 boolean fnd=false;
  936.                 for( int i=0;i<PSize;i++) if( Path[i]==adj) {fnd=true; break;}
  937.                 if( !fnd)
  938.                 {
  939.                     int newPath[]=Path.clone();
  940.                     newPath[PSize]=adj;
  941.                     RecursiveRingFind(newPath,PSize+1,Capacity,RBlk,Rings);
  942.                 }
  943.             }
  944.             return;
  945.         }
  946.        
  947.         // path is full, so make sure it eats its tail
  948.         int last=Path[PSize-1];
  949.         boolean fnd=false;
  950.         for( int n=0;n<graph[last-1].length;n++) if( graph[last-1][n]+1==Path[0]) {fnd=true; break;}
  951.         if( !fnd) return;
  952.        
  953.         // reorder the array then look for duplicates
  954.         int first=0;
  955.         for( int n=1;n<PSize;n++) if( Path[n]<Path[first]) first=n;
  956.         int fm=(first-1+PSize)%PSize,fp=(first+1)%PSize;
  957.         boolean flip=Path[fm]<Path[fp];
  958.         if( first!=0 || flip)
  959.         {
  960.             int newPath[]=new int[PSize];
  961.             for( int n=0;n<PSize;n++) newPath[n]=Path[(first+(flip ? PSize-n : n))%PSize];
  962.             Path=newPath;
  963.         }
  964.                
  965.         for( int n=0;n<Rings.size();n++)
  966.         {
  967.             int[] look=(int[])Rings.get(n);
  968.             boolean same=true;
  969.             for( int i=0;i<PSize;i++) if( look[i]!=Path[i]) {same=false; break;}
  970.             if( same) return;
  971.         }
  972.        
  973.         Rings.add(Path);
  974.     }
  975.    
  976.     // returns the angle maximally equidistant from Th1 and Th2
  977.     double ThetaObtuse(double Th1,double Th2)
  978.     {
  979.         double dth=Th2-Th1;
  980.         while (dth<-Math.PI) dth+=2*Math.PI;
  981.         while (dth>Math.PI) dth-=2*Math.PI;
  982.         return dth>0 ? Th1-0.5*(2*Math.PI-dth) : Th1+0.5*(2*Math.PI+dth);
  983.     }
  984.    
  985.    
  986.     public void RotateMolecule(double Degrees){
  987.         Degrees=MainApplet.rotation;
  988.         System.out.println("rotating..."+Degrees+" degrees");
  989.         double dist;
  990.         double dx;
  991.         double dy;
  992.         double theta;
  993.         double radians=(double)(Degrees*Math.PI/180);
  994.         System.out.println("num atoms= "+NumAtoms());
  995.         for(int n=1;n<=NumAtoms();n++){
  996.             dx=AtomX(n);
  997.             dy=AtomY(n);
  998.             dist=Math.sqrt(dx*dx+dy*dy);
  999.             theta=Math.atan2(dy,dx);
  1000.             try{
  1001.                 SetAtomPos(n,dist*Math.cos(theta+radians),dist*Math.sin(theta+radians));
  1002.             }
  1003.             catch(Exception e){System.out.println("problem with rotation "+e);}
  1004.         }
  1005.     }
  1006. }
  1007.