All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.actelion.research.chem.reaction.FunctionalGroupClassifier Maven / Gradle / Ivy

There is a newer version: 2024.11.2
Show newest version
/*
 * Copyright (c) 1997 - 2016
 * Actelion Pharmaceuticals Ltd.
 * Gewerbestrasse 16
 * CH-4123 Allschwil, Switzerland
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Thomas Sander
 */

package com.actelion.research.chem.reaction;

import com.actelion.research.chem.ExtendedMolecule;
import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.RingCollection;
import com.actelion.research.chem.StereoMolecule;

import java.util.TreeMap;


public class FunctionalGroupClassifier {
	private StereoMolecule mMol;
	private TreeMap mFunctionalGroupCountMap;

	public FunctionalGroupClassifier(StereoMolecule mol) {
		mMol = mol;
		mMol.ensureHelperArrays(Molecule.cHelperParities);
		}

	/**
	 * Applying a predefined dictionary of logically arranged 1024 functional groups
	 * this method determines all functional groups present in the molecule, counts
	 * how often they occurr in the molecule. The first 512 function group IDs refer
	 * to metal-related functions, while the second half of the IDs refer to traditional
	 * organic functional group fragments. These fragments are logically arranged in a tree,
	 * such that functional groups, whose ID only differs in the lowest significant bits,
	 * are structurally related. Functional groups as usually perceived by the chemist are
	 * often described by multiple overlapping fragments.
	 * @return counts mapped to existing functional group IDs
	 */
	public TreeMap getFunctionGroupCountMap() {
		if (mFunctionalGroupCountMap == null)
			classifyFunctionalGroups();

		return mFunctionalGroupCountMap;
		}

	/**
	 * @return int[n][2] n by ID sorted pairs of functional group ID and occurance counts
	 */
	public int[][] getOrganicFunctionalGroupCounts() {
		if (mFunctionalGroupCountMap == null)
			classifyFunctionalGroups();

		int[][] v = new int[mFunctionalGroupCountMap.size()][2];
		int index = 0;
		for (Integer key:mFunctionalGroupCountMap.keySet()) {
			v[index][0] = key;
			v[index][1] = mFunctionalGroupCountMap.get(key).byteValue();
			index++;
			}

		return v;
		}

	/**
	 * Determines the similarity of two functional groups based on their
	 * location in the binary functional group similarity tree.
	 * If the functional belong into the same class (e.g. carbonyl), then the number
	 * of steps upwards in the tree is determined where both groups find a common
	 * still meaningful node. If the functional groups are identical, then 0 is returned.
	 * If they belong into unrelated classes, -1 is returned.
	 * @return -1:unrelated; 0:equal; 1...7:higher value means lower similarity
	 */
	public static int getFunctionalGroupEquivalenceLevel(int fg1, int fg2) {
		if (fg1 == fg2)
			return 0;

		int mask = 1;
		for (int i=1; i<8; i++) {
			if (nodeExists(fg1, i) || nodeExists(fg2, i))
				return -1;

			fg1 |= mask;
			fg2 |= mask;
			if (fg1 == fg2)
				return i;

			mask *= 2;
			}

		return -1;
		}

	private static boolean nodeExists(int fgID, int level) {
		return ClassificationData.getInstance().getEFGName(fgID, 8-level) != null;
		}

	private void classifyFunctionalGroups() {
		mFunctionalGroupCountMap = new TreeMap();

		// mark all atoms that are non-carbon, have pi electrons or are stereo centers
		for (int atm=0; atm 1) {
			for (int i=0; i0)-B(OR)2
						if (mMol.isAllylicAtom( connAtm ))
							return 0x005A;						// C=C-C-B(OR)2
						else
							return 0x005B;					// C(aliph)-B(OR)2
						}
					}
				return 0x005D;										// rare C-BX2
				}
		
			return -2;  // Met-B-Het2
		case 1:
			if (mMol.getConnAtoms( atm ) == 1)
				return 0x004B;											// BH2X
			if (mMol.getConnAtoms( atm ) == 2)
				return 0x004A;											// C-BHX
			if (mMol.getConnAtoms( atm ) == 3)
				{
				switch (mMol.getAtomicNo( hetAtm[0] ))
					{
				case  7: return 0x0050;								// (C-)2B-N
				case  8: return 0x0051;								// (C-)2B-O
				case 16: return 0x0052;								// (C-)2B-S
				default: return 0x0053;						// (C-)2B-X  X!=N,O,S
					}
				}
		
		default:
			switch (getAtomSigma( atm ))
				{
			case  3: return 0x005E;									// (C-)3B
			case  2: return 0x0048;									// (C-)2BH
			default: return 0x0049;										// C-BH2
				}
			}
		}

	private int classSi(int atm) {
		int[] hetAtm = new int[ExtendedMolecule.cMaxConnAtoms];

		mMol.setAtomMarker(atm, false);
		
		if (getAtomPi(atm) != 0) return -2;

		boolean alkinyl = false;
		boolean aryl = false;
		boolean vinyl = false;
		boolean acyl = false;
		boolean allyl = false;
		
		for (int i=0; i 1) {
				for (int j=0; j 1)
								storeEClass(0x0256);				// C=N-CXR2 type
							else if (getAtomSigma( nextCarbon ) > 1)
								storeEClass(0x0252);				// C=N-C(-C)2-R
							else
								storeEClass(0x0253);				// C=N-CH2-R
							}
						}
					}
				}
			}
		
		mMol.setAtomMarker(atm, false);
		
		if (getAtomPi(carbonylC) != 0) {			// C=C=X
			int nextCarbon = getNextConn(carbonylC,atm);
			mMol.setAtomMarker(carbonylC, false);
			mMol.setAtomMarker(nextCarbon, false);
			
			switch (mMol.getAtomicNo( atm ))
				{
			case 7:
				return 0x0218;									// C=C=NR type
			case 8:
				return 0x0219;									// C=C=O
			case 16:
				return 0x021A;									// C=C=S
			default:
				return 0x021B;									// C=C=X  X!=N,O,S
				}
			}
		
		int nrofHets = getHeteros(carbonylC,hetAtm,atm);
		
		switch (mMol.getAtomicNo( atm )) {
		case 7:
			switch(getAtomZ( carbonylC )) {
			case 4:
				if (nrofHets == 1) {
					if (mMol.getAtomicNo( hetAtm[0] ) == 7)
						return 0x021E;							// ?-N=C=N-?
					if (mMol.getAtomicNo( hetAtm[0] ) == 8
					 || mMol.getAtomicNo( hetAtm[0] ) == 16)
						return -1;  							// ?-N=C=O, ?-N=C=S
					return 0x021F;								// ?-N=C=X  X!=N,O,S
					}
		
				if (mMol.getAtomicNo( hetAtm[0] )
				  > mMol.getAtomicNo( hetAtm[1] )) {
					int temp = hetAtm[0];
					hetAtm[0] = hetAtm[1];
					hetAtm[1] = temp;
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 7) {
					switch (mMol.getAtomicNo( hetAtm[1] )) {
					case  7: return 0x0230;						// N-C(=N-?)-N
					case  8: return 0x0231;						// N-C(=N-?)-O
					case 16: return 0x0232;						// N-C(=N-?)-S
					default: return 0x0233;						// N-C(=N-?)-X  X!=S,O,N
						}
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 8) {
					switch (mMol.getAtomicNo( hetAtm[1] )) {
					case  8: return 0x0234;						// O-C(=N-?)-O
					case 16: return 0x0235;						// O-C(=N-?)-S
					default: return 0x0236;						// O-C(=N-?)-X  X!=S,O,N
						}
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 16
				 && mMol.getAtomicNo( hetAtm[1] ) == 16)
					return 0x0238;								// S-C(=N-?)-S
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 16
				 || mMol.getAtomicNo( hetAtm[1] ) == 16)
					return 0x0239;								// S-C(=N-?)-X  X!=S,O,N
		
				return 0x023A;									// Y-C(=N-?)-X  X,Y!=S,O,N
			case 3:
				if (checkAnhydride(hetAtm[0], carbonylC)) {
					if (getAtomSigma( carbonylC ) != 0) {
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x0220;					// C-C(=N-?)-N-?=X
						case  8: return 0x0221;					// C-C(=N-?)-O-?=X
						case 16: return 0x0222;					// C-C(=N-?)-S-?=X
						default: return 0x0223;					// C-C(=N-?)-Y-?=X  Y!=S,O,N
							}
						}
					else
						{
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x0228;					// HC(=N-?)-N-?=X
						case  8: return 0x0229;					// HC(=N-?)-O-?=X
						case 16: return 0x022A;					// HC(=N-?)-S-?=X
						default: return 0x022B;					// ?-N=CH-X-?=Y  X!=S,O,N
							}
						}
					}
				else
					{
					if (getAtomSigma( carbonylC ) != 0) {
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x0224;					// C-C(=N-?)-N  !anhydride Type
						case  8: return 0x0225;					// C-C(=N-?)-O  !anhydride Type
						case 16: return 0x0226;					// C-C(=N-?)-S  !anhydride Type
						default: return 0x0227;					// C-C(=N-?)-X  X!=S,O,N  !aT
							}
						}
					else
						{
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x022C;					// HC(=N-?)-N  !anhydride Type
						case  8: return 0x022D;					// HC(=N-?)-O  !anhydride Type
						case 16: return 0x022E;					// HC(=N-?)-S  !anhydride Type
						default: return 0x022F;					// ?-N=CH-X  X!=S,O,N  !anhydr Type
							}
						}
					}
			default:
				if (getAtomSigma( carbonylC ) == 2) {
					if (mMol.isAllylicAtom( carbonylC ))
						return 0x023C;							// C=C-C(=N-?)-C
					else
						return 0x023D;							// C-C(=N-?)(!allyl)-C
					}
				if (getAtomSigma( carbonylC ) == 1) {
					if (mMol.isAllylicAtom( carbonylC ))
						return 0x023E;							// C=C-CH=N-?
					else
						return 0x023F;							// C-CH(!allyl)=N-?
					}
				return 0x01D4;									// H2C=N-?
				}
		case 8:
			switch(getAtomZ( carbonylC )) {
			case 4:
				if (nrofHets == 1) {
					mMol.setAtomMarker(hetAtm[0], false);
					return 0X021C;								// -N=C=O type
					}
		
				if (mMol.getAtomicNo( hetAtm[0] )
				  > mMol.getAtomicNo( hetAtm[1] )) {
					int temp = hetAtm[0];
					hetAtm[0] = hetAtm[1];
					hetAtm[1] = temp;
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 7) {
					switch (mMol.getAtomicNo( hetAtm[1] )) {
					case  7: return 0x0280;						// N-C(=O)-N
					case  8: return 0x0281;						// N-C(=O)-O
					case 16: return 0x0282;						// N-C(=O)-S
					default: return 0x0283;						// N-C(=O)-X  X!=S,O,N
						}
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 8) {
					switch (mMol.getAtomicNo( hetAtm[1] )) {
					case  8: return 0x0284;							// O-C(=O)-O
					case 16: return 0x0285;							// O-C(=O)-S
					default: return 0x0286;				// O-C(=O)-X  X!=S,O,N
						}
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 16
				 && mMol.getAtomicNo( hetAtm[1] ) == 16)
					return 0x0288;									// S-C(=O)-S
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 16
				 || mMol.getAtomicNo( hetAtm[1] ) == 16)
					return 0x0289;							// S-C(=O)-X  X!=S,O,N
		
				return 0x028A;							// Y-C(=O)-X  X,Y!=S,O,N
			case 3:
				if (checkAnhydride(hetAtm[0],carbonylC)) {
					if (getAtomSigma( carbonylC ) != 0) {
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x0290;					// C-C(=O)-N-?=X
						case  8: return 0x0291;					// C-C(=O)-O-?=X
						case 16: return 0x0292;					// C-C(=O)-S-?=X
						default: return 0x0293;		// C-C(=O)-Y-?=X  Y!=S,O,N
							}
						}
					else {
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x0298;					// HC(=O)-N-?=X
						case  8: return 0x0299;					// HC(=O)-O-?=X
						case 16: return 0x029A;					// HC(=O)-S-?=X
						default: return 0x029B;			// O=CH-X-?=X  X!=S,O,N
							}
						}
					}
				else {
					if (getAtomSigma( carbonylC ) != 0) {
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x0294;	// C-C(=O)-N  !anhydride Type
						case  8: return 0x0295;	// C-C(=O)-O  !anhydride Type
						case 16: return 0x0296;	// C-C(=O)-S  !anhydride Type
						default: return 0x0297;		// C-C(=O)-X  X!=S,O,N  !aT
							}
						}
					else {
						switch (mMol.getAtomicNo( hetAtm[0] )) {
						case  7: return 0x029C;		// HC(=O)-N  !anhydride Type
						case  8: return 0x029D;		// HC(=O)-O  !anhydride Type
						case 16: return 0x029E;		// HC(=O)-S  !anhydride Type
						default: return 0x029F;  // O=CH-X  X!=S,O,N  !anhydr Type
							}
						}
					}
			default:
				if (getAtomSigma( carbonylC ) == 2) {
					if (isQuinone(carbonylC)) return 0x00BC;		// quinone
					if (getCarbons(carbonylC,carbon) != 2) return -2;
					if (mMol.isAromaticAtom( carbon[0] )
					 && mMol.isAromaticAtom( carbon[1] ))
						return 0x02B0;							// Ar-C(=O)-Ar
					int nextCarbon = -1;
					if (mMol.isAromaticAtom( carbon[0] ))
						nextCarbon = carbon[1];
					if (mMol.isAromaticAtom( carbon[1] ))
						nextCarbon = carbon[0];
					if (nextCarbon != -1) {						// Ar-C(=O)-C
						if (getAtomPi(nextCarbon) != 0)
							return 0x02A0;						// Ar-C(=O)-C=C
						switch (getAtomZ( nextCarbon )) {
						case 3:
							return 0x02AC;						// X3C-C(=O)-Ar
						case 2:
							return 0x02AD;						// R-CX2-C(=O)-Ar
						case 1:
							if (getAtomSigma( nextCarbon ) == 3)
								return 0x02AE;				// (C-)2CX-C(=O)-Ar
							else
								return 0x02AF;				// R-CH(-X)-C(=O)-Ar
						default:
							if (getAtomSigma( nextCarbon ) == 4)
								return 0x02B2;				// (C-)3C-C(=O)-Ar
							else
								return 0x02B3;					// R2CH-C(=O)-Ar
							}
						}
					if (getAtomPi(carbon[0]) != 0
					 && getAtomPi(carbon[1]) != 0)
						return 0x02A1;							// C=C-C(=O)-C=C
					nextCarbon = -1;
					if (getAtomPi(carbon[0]) != 0) nextCarbon = carbon[1];
					if (getAtomPi(carbon[1]) != 0) nextCarbon = carbon[0];
					if (nextCarbon != -1) {						// C=C-C(=O)-C(pi=0)
						switch (getAtomZ( nextCarbon )) {
						case 3:
							return 0x02A4;						// X3C-C(=O)-C=C
						case 2:
							return 0x02A5;					// R-CX2-C(=O)-C=C
						case 1:
							if (getAtomSigma( nextCarbon ) == 3)
								return 0x02A6;				// (C-)2CX-C(=O)-C=C
							else
								return 0x02A7;				// R-CH(-X)-C(=O)-C=C
						default:
							if (getAtomSigma( nextCarbon ) == 4)
								return 0x02A2;				// (C-)3C-C(=O)-C=C
							else
								return 0x02A3;					// R2CH-C(=O)-C=C
							}
						}
					int allSigma = getAtomSigma( carbon[0] ) + getAtomSigma( carbon[1] );
					int allZ = getAtomZ( carbon[0] ) + getAtomZ( carbon[1] );
					if (allZ > 4)
						return 0x02A8;						// ?-CX2-C(=O)-CX3
					if (allZ > 2)
						return 0x02A9;					// R-CX2-C(=O)-CX2-R type
					if (allZ != 0) {
						if (allSigma + allZ > 6)
							return 0x02AA;			// C-CHX-C(=O)-CX(-C)2 type
						else
							return 0x02AB;			// R-CH(-X)-C(=O)-CH2-R type
						}
					if (allSigma > 6)
						return 0x02B4;				// (C-)3C-C(=O)-CH(-C)2 type
					else
						return 0x02B5;					// R-CH2-C(=O)-CH2-R type
					}
				if (getAtomSigma( carbonylC ) == 1) {
					int nextCarbon = getNextConn(carbonylC,atm);
					if (mMol.isAromaticAtom( nextCarbon ))
						return 0x01B0;								// Ar-CH=O
					if (getAtomPi(nextCarbon) == 2)
						return 0x01B3;								// C#C-CH=O
					if (getAtomPi(nextCarbon) != 0)
						return 0x01B2;								// C=C-CH=O
					switch (getAtomZ( nextCarbon ))
						{
					case 3:
						return 0x01B8;								// X3C-CH=O
					case 2:
						if (getAtomSigma( nextCarbon ) == 1)
							return 0x01BB;							// X2CH-CH=O
						else
							return 0x01BA;							// C-CX2-CH=O
					case 1:
						if (getAtomSigma( nextCarbon ) == 1)
							return 0x01BC;							// X-CH2-CH=O
						if (getAtomSigma( nextCarbon ) == 2)
							return 0x01BD;						// XCH(-C)-CH=O
						else
							return 0x01BE;						// XC(-C)2-CH=O
					default:
						if (getAtomSigma( nextCarbon ) < 3)
							return 0x01B7;							// RCH2-CH=O
						if (getAtomSigma( nextCarbon ) == 3)
							return 0x01B6;						// (C-)2CH-CH=O
						else
							return 0x01B4;						// (C-)3C-CH=O
						}
					}
				return 0x01D5;											// H2C=O
				}
		case 15:
			return 0x0066;												// C=PH
		case 16:
			switch(getAtomZ( carbonylC ))
				{
			case 4:
				if (nrofHets == 1) {
					mMol.setAtomMarker(hetAtm[0], false);
					return 0x021D;								// -N=C=S type
					}
		
				if (mMol.getAtomicNo( hetAtm[0] )
				  > mMol.getAtomicNo( hetAtm[1] )) {
					int temp = hetAtm[0];
					hetAtm[0] = hetAtm[1];
					hetAtm[1] = temp;
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 7) {
					switch (mMol.getAtomicNo( hetAtm[1] )) {
					case  7: return 0x02C0;							// N-C(=S)-N
					case  8: return 0x02C1;							// N-C(=S)-O
					case 16: return 0x02C2;							// N-C(=S)-S
					default: return 0x02C3;				// N-C(=S)-X  X!=S,O,N
						}
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 8) {
					switch (mMol.getAtomicNo( hetAtm[1] )) {
					case  8: return 0x02C4;							// O-C(=S)-O
					case 16: return 0x02C5;							// O-C(=S)-S
					default: return 0x02C6;				// O-C(=S)-X  X!=S,O,N
						}
					}
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 16
				 && mMol.getAtomicNo( hetAtm[1] ) == 16)
					return 0x02C8;									// S-C(=S)-S
		
				if (mMol.getAtomicNo( hetAtm[0] ) == 16
				 || mMol.getAtomicNo( hetAtm[1] ) == 16)
					return 0x02C9;							// S-C(=S)-X  X!=S,O,N
		
				return 0x02CA;							// Y-C(=S)-X  X,Y!=S,O,N
			case 3:
				if (getAtomSigma( carbonylC ) != 0) {
					switch (mMol.getAtomicNo( hetAtm[0] ))
						{
					case  7: return 0x02D0;							// C-C(=S)-N
					case  8: return 0x02D1;							// C-C(=S)-O
					case 16: return 0x02D2;							// C-C(=S)-S
					default: return 0x02D3;				// C-C(=S)-X  X!=S,O,N
						}
					}
				else {
					switch (mMol.getAtomicNo( hetAtm[0] )) {
					case  7: return 0x02D4;							// HC(=S)-N
					case  8: return 0x02D5;							// HC(=S)-O
					case 16: return 0x02D6;							// HC(=S)-S
					default: return 0x02D7;					// S=CH-X  X!=S,O,N
						}
					}
			default:
				if (getAtomSigma( carbonylC ) == 2) {
					if (mMol.isAllylicAtom( carbonylC ))
						return 0x02CC;							// C=C-C(=S)-C
					else
						return 0x02CD;					// C(pi=0)-C(=S)-C(pi=0)
					}
				if (getAtomSigma( carbonylC ) == 1) {
					if (mMol.isAllylicAtom( carbonylC ))
						return 0x02CE;								// C=C-CH=S
					else
						return 0x02CF;							// C(pi=0)-CH=S
					}
				return 0x01D6;											// H2C=S
				}
		case 34:
			switch(getAtomZ( carbonylC )) {
			case 4:
				return 0x019C;										// X-C(=Se)-X
			case 3:
				if (getAtomSigma( carbonylC ) != 0)
					return 0x019A;									// C-C(=Se)-X
				else
					return 0x019B;									// HC(=Se)-X
			default:
				if (getAtomSigma( carbonylC ) == 2)
					return 0x0198;									// C-C(=Se)-C
				else
					return 0x0199;									// R-CH(=Se)
				}
		default: return -2;
			}
		}

	private int classAs(int atm) {
		mMol.setAtomMarker(atm, false);
		
		if (getAtomSigma( atm ) == 0) {
			switch (getAtomZ( atm )) {
			case 3: return 0x0078;										// AsX3
			case 5: return 0x0079;										// AsX5
			default: return 0x007A;									// rare AsXn
				}
			}
		
		if (getAtomSigma( atm ) == 1) {
			switch (getAtomZ( atm )) {
			case 0: return 0x007C;								// C-AsH2 type
			case 2: return 0x007D;										// C-AsX2
			case 4: return 0x007E;										// C-AsX4
			default: return 0x007F;								// rare C-AsXn
				}
			}
		
		return 0x007B;													// C-As-C
		}

	private int classTe(int atm) {
		mMol.setAtomMarker(atm, false);
		
		if (getAtomSigma( atm ) == 0) {
			switch (getAtomZ( atm )) {
			case 2: return 0x00F8;										// TeX2
			case 4: return 0x00F9;										// TeX4
			case 6: return 0x00FA;										// TeX6
			default: return 0x00FB;									// rare TeXn
				}
			}
		
		if (getAtomSigma( atm ) == 1) {
			switch (getAtomZ( atm )) {
			case 1: return 0x00F4;										// C-Te-X
			case 3: return 0x00F5;										// C-TeX3
			case 5: return 0x00F6;										// C-TeX5
			default: return 0x00F7;								// rare C-TeXn
				}
			}
		
		if (getAtomSigma( atm ) == 2) {
			switch (getAtomZ( atm )) {
			case 0: return 0x00FC;										// C-Te-C
			case 2: return 0x00FD;									// C-TeX2-C
			case 4: return 0x00FE;									// C-TeX4-C
			default: return 0x00FF;								// rare C-TeXn-C
				}
			}
		
		return -1;
		}

	private int classSe(int atm) {
		int[] carbon = new int[ExtendedMolecule.cMaxConnAtoms];
		int[] hetAtm = new int[ExtendedMolecule.cMaxConnAtoms];

		mMol.setAtomMarker(atm, false);
		
		int nrofCarbs = getCarbons(atm,carbon);
		for (int i=0; i1)
						}
				default:
					return 0x0183;							// C-SeX  X!=O,Cl,Se
					}
				}
			else
				return 0x0187;											// X-SeH
			}
		
		if (getAtomZ( atm ) == 2) {
			if (nrofHets == 1
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && getAtomSigma( atm ) == 2) {
				mMol.setAtomMarker(hetAtm[0], false);
				return 0x0188;										// C-Se(=O)-C
				}
			else
				return 0x0189;									// rare Se(z=2)
			}
		
		if (getAtomZ( atm ) == 3) {
			if (nrofHets == 2
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && getAtomSigma( atm ) == 1) {
				mMol.setAtomMarker(hetAtm[0], false);
				if (mMol.getAtomicNo( hetAtm[1] ) == 8) {
					if (mMol.getConnAtoms( hetAtm[1] ) == 1) {
						mMol.setAtomMarker(hetAtm[1], false);
						return 0x0190;							// C-Se(=O)-OH
						}
					else
						return 0x0191;						// C-Se(=O)-O-?  ?!=H
					}
				else
					return 0x0192;							// C-Se(=O)-X  X!=O
				}
			else
				return 0x0193;										// rare SeX3
			}
		
		if (getAtomZ( atm ) == 4) {
			if (nrofHets == 2
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && mMol.getAtomicNo( hetAtm[1] ) == 8
			 && getAtomSigma( atm ) == 2) {
				mMol.setAtomMarker(hetAtm[0], false);
				mMol.setAtomMarker(hetAtm[1], false);
				return 0x018C;										// C-SeO2-C
				}
			if (nrofHets == 3
			&& mMol.getAtomicNo( hetAtm[0] ) == 8) {
				mMol.setAtomMarker(hetAtm[0], false);
				if (mMol.getAtomicNo( hetAtm[1] ) == 8
				 && mMol.getAtomicNo( hetAtm[2] ) == 8)
					return 0x018D;									// O-Se(=O)-O
				else
					return 0x018E;							// X-Se(=O)-X  X!=O
				}
			return 0x018F;											// rare SeX4
			}
		
		if (getAtomZ( atm ) == 5) {
			if (nrofHets == 3
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && mMol.getAtomicNo( hetAtm[1] ) == 8
			 && getAtomSigma( atm ) == 1) {
				mMol.setAtomMarker(hetAtm[0], false);
				mMol.setAtomMarker(hetAtm[1], false);
				if (mMol.getAtomicNo( hetAtm[2] ) == 8) {
					if (mMol.getConnAtoms( hetAtm[2] ) == 1) {
						mMol.setAtomMarker(hetAtm[1], false);
						return 0x0194;								// C-SeO2-OH
						}
					else
						return 0x0195;						// C-SeO2-O-?  ?!=H
					}
				else
					return 0x0196;								// C-SeO2-X  X!=O
				}
			else
				return 0x0197;										// rare SeX5
			}
		
		if (getAtomZ( atm ) == 6) {
			if (nrofHets == 4
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && mMol.getAtomicNo( hetAtm[1] ) == 8
			 && mMol.getAtomicNo( hetAtm[2] ) == 8
			 && mMol.getAtomicNo( hetAtm[3] ) == 8) {
				mMol.setAtomMarker(hetAtm[0], false);
				mMol.setAtomMarker(hetAtm[1], false);
				return 0x018A;										// O-SeO2-O
				}
			return 0x018B;											// rare SeX6
			}
		
		return -1;
		}

	private void classCSe(int atm) {
		if (mMol.isAromaticAtom( atm )) {
			storeEClass(0x01A0);										// Ar-Se
			return;
			}
		if (hasDBondToHetero( atm )) {
			storeEClass(0x01A8);										// X=C-Se
			return;
			}
		if (getAtomZ( atm ) == 1) {
			if (getAtomPi(atm) != 0) {
				if (getAtomSigma( atm ) == 1)
					storeEClass(0x01A2);						// C#C-Se / C=CH-Se
				else
					storeEClass(0x01A3);							// C=C(-C)-Se
				return;
				}
		
			if (mMol.isAllylicAtom( atm ))
				storeEClass(0x01A7);								// C=C-CR2-Se
		
			switch (getAtomSigma( atm )) {
			case 3:
				storeEClass(0x01A4);								// (C-)3C-Se
				return;
			case 2:
				storeEClass(0x01A5);								// (C-)2CH-Se
				return;
			default:
				storeEClass(0x01A6);									// R-CH2-Se
				return;
				}
			}
		if (getAtomZ( atm ) == 2)
			{
			if (getAtomPi(atm) != 0) {
				storeEClass(0x01A1);								// C=C(-X)-Se
				return;
				}
			switch (getAtomSigma( atm )) {
			case 2:
				storeEClass(0x01A9);							// (C-)2C(-X)-Se
				return;
			case 1:
				storeEClass(0x01AA);								// C-CH(-X)-Se
				return;
			default:
				storeEClass(0x01AB);									// X-CH2-Se
				return;
				}
			}
		if (getAtomZ( atm ) == 3) {
			if (getAtomSigma( atm ) != 0)
				storeEClass(0x01AC);									// C-CX2-Se
			else
				storeEClass(0x01AD);									// H-CX2-Se
			return;
			}
		storeEClass(0x01AE);											// CX3-Se
		}

	private int classP(int atm) {
		int[] hetAtm = new int[ExtendedMolecule.cMaxConnAtoms];
		
		mMol.setAtomMarker(atm, false);
		
		int phenyl = 0;
		int vinyl = 0;
		int alkyl = 0;

		for (int i=0; i1)
						}
					}
				else
					return 0x02DB;									// C-S-X  X!=S
				}
			else {
				if (mMol.getConnAtoms( atm ) > 1)
					return -1;					// forget X-S-Met
				return 0x02DE;											// X-SH
				}
			}
		
		if (getAtomZ( atm ) == 2) {
			if (nrofHets == 1
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && getAtomSigma( atm ) == 2) {
				mMol.setAtomMarker(hetAtm[0], false);
				return 0x02E0;										// C-S(=O)-C
				}
			else
				return 0x02E1;									// rare S(z=2)
			}
		
		if (getAtomZ( atm ) == 3) {
			if (nrofHets == 2
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && getAtomSigma( atm ) == 1) {
				mMol.setAtomMarker(hetAtm[0], false);
				if (mMol.getAtomicNo( hetAtm[1] ) == 8)
					return 0x02E8;									// C-S(=O)-O
				else
					return 0x02E9;								// C-S(=O)-X  X!=O
				}
			else
				return 0x02EA;									// rare S(z=3)
			}
		
		if (getAtomZ( atm ) == 4) {
			if (nrofHets == 2
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && mMol.getAtomicNo( hetAtm[1] ) == 8
			 && getAtomSigma( atm ) == 2) {
				mMol.setAtomMarker(hetAtm[0], false);
				mMol.setAtomMarker(hetAtm[1], false);
				return 0x02E4;										// C-SO2-C
				}
			if (nrofHets == 3
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && getAtomSigma( atm ) == 0) {
				mMol.setAtomMarker(hetAtm[0], false);
				if (mMol.getAtomicNo( hetAtm[1] ) == 8
				 && mMol.getAtomicNo( hetAtm[2] ) == 8)
					return 0x02E5;									// O-S(=O)-O
				else
					return 0x02E6;								// X-S(=O)-X  X!=O
				}
			return 0x02E7;										// rare S(z=4)
			}
		
		if (getAtomZ( atm ) == 5) {
			if (nrofHets == 3
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && mMol.getAtomicNo( hetAtm[1] ) == 8
			 && getAtomSigma( atm ) == 1) {
				mMol.setAtomMarker(hetAtm[0], false);
				mMol.setAtomMarker(hetAtm[1], false);
				if (mMol.getAtomicNo( hetAtm[2] ) == 8)
					return 0x02EC;									// C-SO2-O
				else
					return 0x02ED;								// rare C-SO2-X
				}
			else
				return 0x02EE;									// rare S(z=5)
			}
		
		if (getAtomZ( atm ) == 6) {
			if (nrofHets == 4
			 && mMol.getAtomicNo( hetAtm[0] ) == 8
			 && mMol.getAtomicNo( hetAtm[1] ) == 8) {
				mMol.setAtomMarker(hetAtm[0], false);
				mMol.setAtomMarker(hetAtm[1], false);
				return 0x02E2;										// X-SO2-X
				}
			return 0x02E3;										// rare S(z=6)
			}
		
		return -1;
		}

	private void classCS(int carbon, int sulfur) {
		int[] hetAtm = new int[ExtendedMolecule.cMaxConnAtoms];
		
		if (bondToMet(carbon)) return;
		
		if (mMol.isAromaticAtom( carbon )) {
			storeEClass(0x01C0);									// C(arom)-S
			return;
			}
		
		if (getAtomPi(carbon) == 2) {
			storeEClass(0x01C1);										// C#C-S
			return;
			}
		
		int[] tripleBnds = new int[1];
		int[] doubleBnds = new int[1];
		int nrofHets = getSortedHeteros(carbon,hetAtm, tripleBnds,doubleBnds,sulfur);
		
		if (tripleBnds[0] != 0) {
			storeEClass(0x01D0);										// X#C-S
			return;
			}
		
		if (hasDBondToHetero( carbon )) {
			storeEClass(0x01D1);										// X=C-S
			return;
			}
		
		if (getAtomPi(carbon) == 1) {
			if (getAtomZ( carbon ) == 2) {
				if (mMol.getAtomicNo( hetAtm[0] ) == 16)
					storeEClass(0x01C8);								// C=C(S)-S
				else
					storeEClass(0x01C9);							// C=CX-S  X!=S
				}
			else {
				if (getAtomSigma( carbon ) == 2)
					storeEClass(0x01C2);							// C=C(-C)-S
				else
					storeEClass(0x01C3);								// C=CH-S
				}
			return;
			}
		
		if (getAtomZ( carbon ) == 1) {
			if (mMol.isAllylicAtom( carbon ))
				storeEClass(0x01C7);								// C=C-CR2-S
		
			switch (getAtomSigma( carbon )) {
			case 3:
				storeEClass(0x01C6);									// (C-)3C-S
				return;
			case 2:
				storeEClass(0x01C5);								// (C-)2CH-S
				return;
			default:
				storeEClass(0x01C4);									// R-CH2-S
				return;
				}
			}
		
		if (getAtomZ( carbon ) == 2) {
			if (getAtomSigma( carbon ) == 0) {
				if (mMol.getAtomicNo( hetAtm[0] ) == 16)
					storeEClass(0x01CE);								// S-CH2-S
				else
					storeEClass(0x01CF);							// X-CH2-S  X!=S
				return;
				}
			if (getAtomSigma( carbon ) == 1) {
				if (mMol.getAtomicNo( hetAtm[0] ) == 16)
					storeEClass(0x01CC);							// S-CH(-C)-S
				else
					storeEClass(0x01CD);						// X-CH(-C)-S  X!=S
				return;
				}
			if (getAtomSigma( carbon ) == 2) {
				if (mMol.getAtomicNo( hetAtm[0] ) == 16)
					storeEClass(0x01CA);							// S-C(-C)2-S
				else
					storeEClass(0x01CB);						// X-C(-C)2-S  X!=S
				return;
				}
			}
		
		if (getAtomZ( carbon ) == 3)
			{
			storeEClass(0x01D2);										// R-CX2-S
			return;
			}
		
		if (getAtomZ( carbon ) == 4)
			{
			storeEClass(0x01D3);										// S-CX3
			}
		
		return;
		}

	private int classI(int atm) {
		final int[] iodineClass = {
		 0x0160,						// Ar-I
		 0x0161,						// C#C-I
		 0x0163,					// C=CH-I
		 0x0162,					// C=C(-C)-I
		 0x0164,					// C=C-CR2-I
		 0x0167,		// non allylic (C-)3C-I
		 0x0166,		// non allylic (C-)2CH-I
		 0x0165,		// non allylic R-CH2-I
		 0x0168,					// C=CX-I
		 0x0169,					// C=C-CX-I
		 0x016A,		// non allylic R2CX-I
		 0x0170,					// R-CO-I
		 0x0172,					// R-C(=N)-I
		 0x0171,			// R-C(=X)-I  X!=O,N
		 0x016C,					// I3C-X=Y
		 0x016D,				// I3C-R(!stab)
		 0x016E,		// stabilized I-CX2-R
		 0x016F,	// non stabilized I-CX2-R
		 0x0173,						// I-C#N
		 0x0174,					// I-C(=O)-X
		 0x0175,					// I-C(=N)-X
		 0x0176,			// I-C(=X)-Y  X!=N,O
		 0x0177,						// I-CX3
		 0x017C,				// I at amide N
		 0x017D,				// I at amine N
		 0x0179,				// rare I-O-X
		 0x0178,						// I-O-C
		 0x017E,					// I at B
		 0x017F,					// I at Si
		 0x017A,					// I at P
		 0x017B						// I at S
		  };
		int eClass;
		
		mMol.setAtomMarker(atm, false);
		
		if (mMol.getConnAtoms( atm ) > 1)		// more than one neighbors to iodine
			return 0x016B;
		
		eClass = classHal(atm,53);
		if (eClass < 0) return eClass;
		return iodineClass[eClass];
		}

	private int classBr(int atm) {
		final int[] bromineClass = {
		 0x0140,						// Ar-Br
		 0x0141,						// C#C-Br
		 0x0143,					// C=CH-Br
		 0x0142,					// C=C(-C)-Br
		 0x0144,					// C=C-CR2-Br
		 0x0147,		// non allylic (C-)3C-Br
		 0x0146,		// non allylic (C-)2CH-Br
		 0x0145,		// non allylic R-CH2-Br
		 0x0148,					// C=CX-Br
		 0x0149,					// C=C-CX-Br
		 0x014A,		// non allylic R2CX-Br
		 0x0150,					// R-CO-Br
		 0x0152,					// R-C(=N)-Br
		 0x0151,			// R-C(=X)-Br  X!=O,N
		 0x014C,					// Br3C-X=Y
		 0x014D,				// Br3C-R(!stab)
		 0x014E,		// stabilized Br-CX2-R
		 0x014F,	// non stabilized Br-CX2-R
		 0x0153,						// Br-C#N
		 0x0154,					// Br-C(=O)-X
		 0x0155,					// Br-C(=N)-X
		 0x0156,			// Br-C(=X)-Y  X!=N,O
		 0x0157,						// Br-CX3
		 0x015C,				// Br at amide N
		 0x015D,				// Br at amine N
		 0x0159,				// rare Br-O-X
		 0x0158,						// Br-O-C
		 0x015E,					// Br at B
		 0x015F,					// Br at Si
		 0x015A,					// Br at P
		 0x015B						// Br at S
		  };
		int eClass;
		
		mMol.setAtomMarker(atm, false);
		
		if (mMol.getConnAtoms( atm ) > 1)	// more than one neighbors to bromine
			return -2;
		
		eClass = classHal(atm,35);
		if (eClass < 0) return eClass;
		return bromineClass[eClass];
		}

	private int classCl(int atm) {
		final int[] chlorineClass = {
		 0x0120,						// Ar-Cl
		 0x0121,						// C#C-Cl
		 0x0123,					// C=CH-Cl
		 0x0122,					// C=C(-C)-Cl
		 0x0124,					// C=C-CR2-Cl
		 0x0127,		// non allylic (C-)3C-Cl
		 0x0126,		// non allylic (C-)2CH-Cl
		 0x0125,		// non allylic R-CH2-Cl
		 0x0128,					// C=CX-Cl
		 0x0129,					// C=C-CX-Cl
		 0x012A,		// non allylic R2CX-Cl
		 0x0130,					// R-CO-Cl
		 0x0132,					// R-C(=N)-Cl
		 0x0131,			// R-C(=X)-Cl  X!=O,N
		 0x012C,					// Cl3C-X=Y
		 0x012D,				// Cl3C-R(!stab)
		 0x012E,		// stabilized Cl-CX2-R
		 0x012F,	// non stabilized Cl-CX2-R
		 0x0133,						// Cl-C#N
		 0x0134,					// Cl-C(=O)-X
		 0x0135,					// Cl-C(=N)-X
		 0x0136,			// Cl-C(=X)-Y  X!=N,O
		 0x0137,						// Cl-CX3
		 0x013C,				// Cl at amide N
		 0x013D,				// Cl at amine N
		 0x0139,				// rare Cl-O-X
		 0x0138,						// Cl-O-C
		 0x013E,					// Cl at B
		 0x013F,					// Cl at Si
		 0x013A,					// Cl at P
		 0x013B						// Cl at S
		  };
		int eClass;
		
		mMol.setAtomMarker(atm, false);
		
		if (mMol.getConnAtoms( atm ) > 1)	// more than one neighbors to chlorine
			return -2;
		
		eClass = classHal(atm,17);
		if (eClass < 0) return eClass;
		return chlorineClass[eClass];
		}

	private int classF(int atm) {
		final int[] fluorineClass = {
		 0x0100,						// Ar-F
		 0x0101,						// C#C-F
		 0x0103,					// C=CH-F
		 0x0102,					// C=C(-C)-F
		 0x0104,					// C=C-CR2-F
		 0x0107,		// non allylic (C-)3C-F
		 0x0106,		// non allylic (C-)2CH-F
		 0x0105,		// non allylic R-CH2-F
		 0x0108,					// C=CX-F
		 0x0109,					// C=C-CX-F
		 0x010A,		// non allylic R2CX-F
		 0x0110,					// R-CO-F
		 0x0112,					// R-C(=N)-F
		 0x0111,			// R-C(=X)-F  X!=O,N
		 0x010C,					// F3C-X=Y
		 0x010D,				// F3C-R(!stab)
		 0x010E,		// stabilized F-CX2-R
		 0x010F,	// non stabilized F-CX2-R
		 0x0113,						// F-C#N
		 0x0114,					// F-C(=O)-X
		 0x0115,					// F-C(=N)-X
		 0x0116,			// F-C(=X)-Y  X!=N,O
		 0x0117,						// F-CX3
		 0x011C,				// F at amide N
		 0x011D,				// F at amine N
		 0x0119,				// rare F-O-X
		 0x0118,						// F-O-C
		 0x011E,					// F at B
		 0x011F,					// F at Si
		 0x011A,					// F at P
		 0x011B						// F at S
		  };
		int eClass;
		
		mMol.setAtomMarker(atm, false);
		
		if (mMol.getConnAtoms( atm ) > 1)	// more than one neighbors to fluorine
			return -2;
		
		eClass = classHal(atm,9);
		if (eClass < 0) return eClass;
		return fluorineClass[eClass];
		}

	private int classHal(int atm, int halType) {
		int[] hetAtm = new int[3];
		
		if (mMol.getConnAtoms( atm ) != 1)	// HHal not classified
			return -2;
		
		int connAtm = mMol.getConnAtom( atm, 0 );
		if (mMol.getAtomicNo( connAtm ) == 6) {		// halogene at carbon
			if (bondToMet(connAtm)) return -1;
		
			if (mMol.isAromaticAtom( connAtm ))
				return 0;						// aryl halide
		
			switch(getAtomZ( connAtm )) {
			case 1:											// one hetero atom at carbon
				if (getAtomPi(connAtm) == 2)
					return 1;					// alkinyl halide
				if (getAtomPi(connAtm) == 1) {
					if (getAtomSigma( connAtm ) == 1)
						return 2;				// C=CH-Hal
					else
						return 3;				// C=C(-C)-Hal
					}
				if (mMol.isAllylicAtom( connAtm )) {
					return 4;					// allylic halide
					}
				else {
					switch(getAtomSigma( connAtm )) {
					case 3: return 5;			// !allylic tert. alkyl halide
					case 2: return 6;			// !allylic sec.  alkyl halide
					default: return 7;			// !allylic prim. alkyl halide
						}
					}
			case 2:											// two hetero atoms at carbon
				if (getHeteros(connAtm,hetAtm,atm) != 1) return -2;
		
				if (getAtomPi(connAtm) != 0)
					return 8;					// 1-halo-1-hetero-1-alkene
				if (mMol.isAllylicAtom( connAtm ))
					return 9;					// allylic 1-halo-1-hetero-alkane
				else
					return 10;					// !allylic 1-halo-1-hetero-alkane
			case 3:											// three hetero atoms at carbon
				int nrofHets = getHeteros(connAtm,hetAtm,atm);
				if (nrofHets == 1) {						// double bond to hetero atom
					if (mMol.getAtomicNo( hetAtm[0] ) == 8) {
						mMol.setAtomMarker(hetAtm[0], false);
						return 11;				// R-CO-Hal
						}
					if (mMol.getAtomicNo( hetAtm[0] ) == 7) {
						return 12;				// Hal-C(=N)-R
						}
					else return 13;				// Hal-C(z=3)=X  X!=O,N
					}
				else {										// three single bonded hetero atoms
					if (mMol.getAtomicNo( hetAtm[0] ) == halType
					 && mMol.getAtomicNo( hetAtm[1] ) == halType) {
						mMol.setAtomMarker(hetAtm[0], false);
						mMol.setAtomMarker(hetAtm[1], false);
						if (mMol.isStabilizedAtom( connAtm ))
							return 14;			// Hal3C-X=Y
						else
							return 15;			// Hal3C(!stab)
						}
					if (mMol.isStabilizedAtom( connAtm ))
						return 16;				// stabilized Hal-CX2
					else
						return 17;				// !stabilized Hal-CX2
					}
			case 4:											// four hetero atoms at carbon
				int[] tripleBnds = new int[1];
				int[] doubleBnds = new int[1];
				nrofHets = getSortedHeteros(connAtm,hetAtm, tripleBnds,doubleBnds,atm);
				if (nrofHets == 1) {						// triple bond to hetero atom
					if (mMol.getConnAtoms( hetAtm[0] ) == 2)
						mMol.setAtomMarker(hetAtm[0], false);
					return 18;					// Hal-C#N
					}
				if (nrofHets == 2) {						// double bond to hetero atom
					if (mMol.getAtomicNo( hetAtm[0] ) == 8)
						return 19;				// Hal-C(=O)-Het
					if (mMol.getAtomicNo( hetAtm[0] ) == 7)
						return 20;				// Hal-C(=N)-Het
					else
						return 21;				// Hal-C(=X)-Het  X!=N,O
					}
				if (nrofHets == 3)				// three single bonded hetero atoms
					return 22;					// Hal-CHet3
			default: return -2;
				}
			}
		
		if (mMol.getAtomicNo( connAtm ) == 7) {		// halogene at nitrogen
			if (mMol.isStabilizedAtom( connAtm ))
				return 23;						// Hal-N(stab)
			else
				return 24;						// Hal-N(!stab)
			}
		
		if (mMol.getAtomicNo( connAtm ) == 8) {		// halogene at oxygen
			int nextConn = getNextConn(connAtm,atm);

			if (nextConn == -1 || mMol.getAtomicNo( nextConn ) == 6)
				return 26;						// Hal-O-C, Hal-OH
			else
				return 25;						// Hal-O-X  X=Met,Het
			}
		
		if (mMol.getAtomicNo( connAtm ) == 5)
			return 27;							// halogene at boron
		
		if (mMol.getAtomicNo( connAtm ) == 14)
			return 28;							// halogene at silicon
		
		if (mMol.getAtomicNo( connAtm ) == 15)
			return 29;							// halogene at phosphorous
		
		if (mMol.getAtomicNo( connAtm ) == 16)
			return 30;							// halogene at sulfur
		
		return -2;
		}

	private int classN(int atm) {
		int[] conn = new int[ExtendedMolecule.cMaxConnAtoms];
		int[] hetAtm = new int[ExtendedMolecule.cMaxConnAtoms];
		
		mMol.setAtomMarker(atm, false);
		
		if (mMol.getConnAtoms( atm ) == 0)	// don't handle NH3
			return -2;
		
		for (int i=0; i 1) {
				if (mMol.getAtomicNo( conn[0] ) == 6						// aziridine
				 && mMol.getAtomicNo( conn[1] ) == 6) {
					if (getAtomPi(conn[0]) != 0
					 || getAtomPi(conn[1]) != 0)
						return 0x02BC;					// alkylidene aziridine
					if (getAtomZ( conn[0] ) == 1
					 && getAtomZ( conn[1] ) == 1)
						{
						if (mMol.isAllylicAtom( conn[0] )
						 || mMol.isAllylicAtom( conn[1] ))
							return 0x02BD;					// alkenyl aziridine
						else
							return 0x02BF;					// alkyl aziridine
						}
					return 0x02BE;							// hetero aziridine
					}
				return 0x00BD;								// dihetero 3-ring
				}
			}
		
		for (int i=0; i class2) {
			int temp = class1;
			class1 = class2;
			class2 = temp;
			}
		
		return etherClass[base[class1]+class2];
		}

	private int classAtmO(int atm) {
		if (mMol.getAtomicNo( atm ) == 6) {
			if (mMol.isAromaticAtom( atm )) return 0;			// Ar-O
			if (hasDBondToHetero( atm )) return 1;	// X=C-O
			if (getAtomZ( atm ) == 1) {
				if (getAtomPi(atm) != 0) {
					if (getAtomSigma( atm ) == 1)
						return 2;								// C#C-O / C=CH-O
					else
						return 3;									// C=C(-C)-O
					}
				switch (getAtomSigma( atm )) {
				case 3:  return 4;									// (C-)3C-O
				case 2:  return 5;									// (C-)2CH-O
				case 1:  return 6;									// C-CH2-O
				default: return 7;										// CH3-O
					}
				}
			if (getAtomZ( atm ) == 2) {
				if (getAtomPi(atm) != 0)
					return 8;										// C=C(-X)-O
				switch (getAtomSigma( atm )) {
				case 2:  return  9;								// (C-)2C(-X)-O
				case 1:  return 10;								// C-CH(-X)-O
				default: return 11;									// X-CH2-O
					}
				}
			if (getAtomZ( atm ) == 3) {
				if (getAtomSigma( atm ) != 0)
					return 12;										// C-CX2-O
				else
					return 13;										// H-CX2-O
				}
			return 14;													// CX3-O
			}
		else {
			switch (mMol.getAtomicNo( atm )) {
			case  5: return 15;											// B-O
			case  7: return 16;											// N-O
			case  8: return 17;											// O-O
			case 14: return 18;											// Si-O
			case 15: return 19;											// P-O
			case 16: return 20;											// S-O
			default: return 21;										// rare X-O
				}
			}
		}

	private int classC(int atm) {
		final int[][] alkyneClass = {	{ -1,0x00C0,0x00C2,0x00C1,0x00C3 },
										{ 0x00C0,0x00C8,0x00CA,0x00C9,0x00CB },
										{ 0x00C2,0x00CA,0x00C4,0x00C5,0x00C6 },
										{ 0x00C1,0x00C9,0x00C5,0x00CC,0x00CD },
										{ 0x00C3,0x00CB,0x00C6,0x00CD,0x00C7 } };
		
		mMol.setAtomMarker(atm, false);
		
		if (isThreeRingAtom( atm )) {
			int[] member = new int[3];
			getRingMembers( atm, 3, false, member );
			if (mMol.getAtomicNo( member[1] ) != 6
			 || mMol.getAtomicNo( member[2] ) != 6) return -2;
			boolean vinyl = false;
			boolean stab = false;
			boolean allyl = false;
			for (int i=0; i<3; i++) {
				mMol.setAtomMarker(member[i], false);
				if (getAtomPi(member[i]) != 0) vinyl = true;
				if (mMol.isStabilizedAtom( member[i] )) stab = true;
				if (mMol.isAllylicAtom( member[i] )) allyl = true;
				}
			if (vinyl) return 0x00E4;				// C3-ring with double bond
			if (stab)  return 0x00E6;						// stabilized C3-ring
			if (allyl) return 0x00E7;							// allylic C3-ring
			return 0x00E5;								// featureless C3-ring
			}
		
		if (getAtomPi(atm) == 0) {
			if (mMol.getAtomParity( atm ) == 0)
				return -2;
		
			if (getAtomSigma( atm ) + getAtomZ( atm ) == 4)
				return 0x00F0;									// XnC*(-C)4-n
			if (mMol.isStabilizedAtom( atm ))
				return 0x00F1;										// X=?-C*H
			if (mMol.isAllylicAtom( atm ))
				return 0x00F2;										// C=C-C*H
			return 0x00F3;									// featureless C*H
			}

		int[] member = new int[ExtendedMolecule.cMaxConnAtoms];
		int nrofPiConns = getPiConnCarbs(atm,member);
		if (getAtomPi(atm) == 2 && nrofPiConns == 1) {				// C#C
			mMol.setAtomMarker(member[0], false);
			int class1 = classAcetylen(atm,member[0]);
			int class2 = classAcetylen(member[0],atm);
			if (class1 == 5 || class2 == 5) return 0x00CE;			// ?-C#C-B
			if (class1 == 6 || class2 == 6) return 0x00CF;	/*?-C#C-Met  Met,?!=B*/
			return alkyneClass[class1][class2];
			}
		
		if (bondToMet(atm)) return -1;
		for (int i=0; i 2) {
			if (stab != 0) return 0x00DF;								// X=?-XC=CX2
			else		return 0x00E0;									// X2C=CX-?
			}
		if (hetero == 2) {
			if (stab != 0) return 0x00DE;							// X=?-XC=CX-? type
			else		return 0x00E1;							// ?-XC=CX-? type
			}
		if (stab > 2) return 0x00D8;						// (X=?-)2C=C-?=X type
		if (stab == 2) {
			if (hetero != 0) return 0x00DC;						// X=?-XC=C-?=X type
			else		return 0x00D9;						// X=?-RC=CR-?=X type
			}
		if (hetero == 1) {
			if (stab == 1) return 0x00DD;						// X=?-C=CX type
			if (vinyl + alkyl + phenyl >1) return 0x00E3;		// C-C=CX-C type
			return 0x00E2;										// R-HC=CH-X type
			}
		if (stab == 1) {
			if (vinyl + alkyl + phenyl >1) return 0x00DA;   // C-C=C(-C)-?=X type
			return 0x00DB;									// R-HC=CH-C=X type
			}
		switch (vinyl + alkyl + phenyl) {
		case 4:
			if (alkyl == 4) return 0x00D6;						// (C-)2C=C(-C)2
			return 0x00D7;									// C=C-C(-C)=C(-C)2
		case 3:
			if (alkyl == 3) return 0x00D4;						// C-CH=C(-C)2
			return 0x00D5;									// C=C-CH=C(-C)2 type
		case 2:
			if (alkyl == 2) return 0x00D2;						// C-CH=CH-C type
			return 0x00D3;									// C=C-CH=CH-C type
		default:
			if (alkyl == 1) return 0x00D0;							// C-CH=CH2
			return 0x00D1;											// C=C-CH=CH2
			}
		}

	private int classAcetylen(int atm, int other) {
		int nextConn;
		
		if (mMol.getConnAtoms( atm ) == 1) return 0;			// C#C-H
		nextConn = getNextConn(atm,other);
		
		if (mMol.isAromaticAtom( nextConn )) return 1;			// C#C-C=X
		if (mMol.isElectronegative( nextConn )) return 2;			// C#C-X
		
		if (mMol.isElectropositive( nextConn )) {
			if (mMol.getAtomicNo( nextConn ) == 5) return 5;	// C#C-B
			else								return 6;   	// ?-C#C-Met  Met,?!=B
			}
		
		if (getAtomPi(nextConn) != 0) return 3;						// C#C-C=C
			return 4;											// C#C-C(aliphatic)
		}

	private void storeEClass( int eClass ) {
		if (eClass == -1) return;					// FG was ealier classified by another modul
		
		if (eClass == -2) return;					// don't care about FG recognition
													// error here; just don't store FG

		Integer count = mFunctionalGroupCountMap.get(eClass);
		mFunctionalGroupCountMap.put(eClass, count == null ? 1 : count + 1);

		return;
		}

	private int getPiConnCarbs( int atm, int[] member) {
		int count = 0;
		for (int i=0; i 1) {
				int connAtm = mMol.getConnAtom( atm, i );
				if (mMol.getAtomicNo( connAtm ) == 6)
					member[count++] = connAtm;
				}
			}
		return count;
		}

	/**
	 * Returns all ring member atoms
	 * @param atom
	 * @param ringSize if 0 then return ring of any size
	 * @param aromatic consider aromatic rings only
	 * @param member
	 * @return
	 */
	private int getRingMembers( int atom, int ringSize, boolean aromatic, int member[] ) {
		RingCollection ringSet = mMol.getRingSet();
		
		member[0] = atom;
		for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy