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

com.actelion.research.chem.Molecule 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.
*
*/

package com.actelion.research.chem;

import com.actelion.research.gui.generic.GenericRectangle;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;


public class Molecule implements Serializable {
	// We need the serialVersionUID in order to make sure, that after obfuscation the Molecule keeps
	// the same serialVersionUID since the inter-JVM drag & drop checks the Serializable version ID
	// which is either generated at via hash or can explicitly set via serialVersionUID. Due to the
	// fact, that the obfuscation process alters member/type names the generated hash is going to
	// be changed as well.
	// In addition to the above requirement, the class name should not be obfuscated at all!

//	static final long serialVersionUID = 0x20220517;	// after extending atom query features from int to long
	static final long serialVersionUID = 0x20221130;	// after introducing quadruple and quintuple bonds

	public static final int cMaxAtomicNo = 190;

		// parity based on atom positions in atom table (as MDL parity)
	protected static final int cAtomFlagsParity		= 0x000003;
	public static final int cAtomParityNone			= 0x000000;
	public static final int cAtomParity1			= 0x000001;
	public static final int cAtomParity2			= 0x000002;
	public static final int cAtomParityUnknown		= 0x000003;

	public static final int cAtomParityIsPseudo		= 0x000004;
	protected static final int cAtomFlagSmallRing	= 0x000008;

	public static final int cAtomRadicalState		= 0x000030;
	public static final int cAtomRadicalStateShift	= 4;
	public static final int cAtomRadicalStateNone	= 0x000000;
	public static final int cAtomRadicalStateS		= 0x000010;
	public static final int cAtomRadicalStateD		= 0x000020;
	public static final int cAtomRadicalStateT		= 0x000030;

	private static final int cAtomFlagsColor		= 0x0001C0;
	public static final int cAtomColorNone			= 0x000000;
	public static final int cAtomColorBlue			= 0x000040;
	public static final int cAtomColorRed			= 0x000080;
	public static final int cAtomColorGreen			= 0x0000C0;
	public static final int cAtomColorMagenta		= 0x000100;
	public static final int cAtomColorOrange		= 0x000140;
	public static final int cAtomColorDarkGreen		= 0x000180;
	public static final int cAtomColorDarkRed		= 0x0001C0;
	private static final int cAtomFlagSelected 		= 0x000200;

	protected static final int cAtomFlagsHelper2	= 0x00003C08;
	protected static final int cAtomFlagsHelper3	= 0x0401C007;
	protected static final int cAtomFlagsHelper		= cAtomFlagsHelper2 | cAtomFlagsHelper3;

	protected static final int cAtomFlagsRingBonds	= 0x000C00;
	protected static final int cAtomFlags2RingBonds = 0x000400;
	protected static final int cAtomFlags3RingBonds = 0x000800;
	protected static final int cAtomFlags4RingBonds = 0x000C00;
	protected static final int cAtomFlagAllylic		= 0x001000;
	protected static final int cAtomFlagStabilized	= 0x002000;

	private static final int cAtomFlagsCIPParity	= 0x00C000;
	private static final int cAtomFlagsCIPParityShift = 14;
	public static final int cAtomCIPParityNone		= 0x000000;
	public static final int cAtomCIPParityRorM		= 0x000001;
	public static final int cAtomCIPParitySorP		= 0x000002;
	public static final int cAtomCIPParityProblem	= 0x000003;

	protected static final int cAtomFlagStereoProblem = 0x010000;
	protected static final int cAtomFlagMarked		= 0x020000;

		// MDL's enhanced stereochemical representation (ESR group and type may be assigned
		// to TH and allene stereo centers as well as to BINAP kind of stereo bonds)
	public static final int cESRTypeAbs				= 0;
	public static final int cESRTypeAnd				= 1;
	public static final int cESRTypeOr				= 2;
	public static final int cESRMaxGroups			= 32;
	public static final int cESRGroupBits			= 5;

	protected static final int cAtomFlagsESR		= 0x01FC0000;
	private static final int cAtomFlagsESRType		= 0x000C0000;
	private static final int cAtomFlagsESRTypeShift = 18;
	private static final int cAtomFlagsESRGroup		= 0x01F00000;
	private static final int cAtomFlagsESRGroupShift = 20;

	protected static final int cAtomFlagConfigurationUnknown = 0x02000000;
	private static final int cAtomFlagIsStereoCenter = 0x04000000;

	protected static final int cAtomFlagsValence	= 0x78000000;
	private static final int cAtomFlagsValenceShift = 27;

	public static final int cAtomQFNoOfBits			= 46;
	public static final int cAtomQFAromStateBits	= 2;
	public static final int cAtomQFAromStateShift	= 1;
	public static final int cAtomQFRingStateBits	= 4;
	public static final int cAtomQFRingStateShift	= 3;
	public static final int cAtomQFHydrogenBits		= 4;
	public static final int cAtomQFHydrogenShift	= 7;
	public static final int cAtomQFPiElectronBits	= 3;
	public static final int cAtomQFPiElectronShift	= 14;
	public static final int cAtomQFNeighbourBits	= 5;
	public static final int cAtomQFNeighbourShift	= 17;
	public static final int cAtomQFSmallRingSizeBits = 3;
	public static final int cAtomQFSmallRingSizeShift = 22;
	public static final int cAtomQFChargeBits		= 3;
	public static final int cAtomQFChargeShift		= 25;
	public static final int cAtomQFRxnParityBits	= 2;
	public static final int cAtomQFRxnParityShift	= 30;
	public static final int cAtomQFNewRingSizeBits	= 7;
	public static final int cAtomQFNewRingSizeShift = 32;
	public static final int cAtomQFENeighbourBits	= 5;
	public static final int cAtomQFENeighbourShift	= 39;
	public static final int cAtomQFStereoStateBits	= 2;
	public static final int cAtomQFStereoStateShift = 44;
	public static final long cAtomQFSimpleFeatures	= 0x00007F800E3FC7FEL;
	public static final long cAtomQFNarrowing		= 0x00007FFF0FFFFFFEL;
	public static final long cAtomQFAny				= 0x00000001;
	public static final long cAtomQFAromState		= 0x0000400000000006L;
	public static final long cAtomQFAromatic		= 0x00000002;
	public static final long cAtomQFNotAromatic		= 0x00000004;
	public static final long cAtomQFRingState		= 0x00000078;
	public static final long cAtomQFNotChain		= 0x00000008;
	public static final long cAtomQFNot2RingBonds	= 0x00000010;
	public static final long cAtomQFNot3RingBonds	= 0x00000020;
	public static final long cAtomQFNot4RingBonds	= 0x00000040;
	public static final long cAtomQFHydrogen		= 0x00000780;
	public static final long cAtomQFNot0Hydrogen	= 0x00000080;
	public static final long cAtomQFNot1Hydrogen	= 0x00000100;
	public static final long cAtomQFNot2Hydrogen	= 0x00000200;
	public static final long cAtomQFNot3Hydrogen	= 0x00000400;
	public static final long cAtomQFNoMoreNeighbours= 0x00000800;
	public static final long cAtomQFMoreNeighbours	= 0x00001000;
	public static final long cAtomQFMatchStereo		= 0x00002000;
	public static final long cAtomQFPiElectrons		= 0x0001C000;
	public static final long cAtomQFNot0PiElectrons = 0x00004000;
	public static final long cAtomQFNot1PiElectron  = 0x00008000;
	public static final long cAtomQFNot2PiElectrons = 0x00010000;
	public static final long cAtomQFNeighbours		= 0x003E0000;  // these QF refer to non-H neighbours
	public static final long cAtomQFNot0Neighbours  = 0x00020000;
	public static final long cAtomQFNot1Neighbour	= 0x00040000;
	public static final long cAtomQFNot2Neighbours  = 0x00080000;
	public static final long cAtomQFNot3Neighbours  = 0x00100000;
	public static final long cAtomQFNot4Neighbours  = 0x00200000;  // this is not 4-or-more neighbours
	public static final long cAtomQFSmallRingSize   = 0x01C00000;  // legacy: used to just define the smallest ring an atom is member of
	public static final long cAtomQFCharge			= 0x0E000000;
	public static final long cAtomQFNotChargeNeg	= 0x02000000;
	public static final long cAtomQFNotCharge0		= 0x04000000;
	public static final long cAtomQFNotChargePos	= 0x08000000;
	public static final long cAtomQFFlatNitrogen	= 0x0000000010000000L;  // Currently, only used in TorsionDetail
	public static final long cAtomQFExcludeGroup	= 0x0000000020000000L;  // These atoms must not exist in SS-matches
	public static final long cAtomQFRxnParityHint   = 0x00000000C0000000L;  // Retain,invert,racemise configuration in reaction
	public static final long cAtomQFRxnParityRetain = 0x0000000040000000L;
	public static final long cAtomQFRxnParityInvert = 0x0000000080000000L;
	public static final long cAtomQFRxnParityRacemize=0x00000000C0000000L;
	public static final long cAtomQFNewRingSize     = 0x0000007F00000000L;
	public static final long cAtomQFRingSize0       = 0x0000000100000000L;
	public static final long cAtomQFRingSize3       = 0x0000000200000000L;
	public static final long cAtomQFRingSize4       = 0x0000000400000000L;
	public static final long cAtomQFRingSize5       = 0x0000000800000000L;
	public static final long cAtomQFRingSize6       = 0x0000001000000000L;
	public static final long cAtomQFRingSize7       = 0x0000002000000000L;
	public static final long cAtomQFRingSizeLarge   = 0x0000004000000000L;
	public static final long cAtomQFENeighbours     = 0x00000F8000000000L;
	public static final long cAtomQFNot0ENeighbours = 0x0000008000000000L;
	public static final long cAtomQFNot1ENeighbour  = 0x0000010000000000L;
	public static final long cAtomQFNot2ENeighbours = 0x0000020000000000L;
	public static final long cAtomQFNot3ENeighbours = 0x0000040000000000L;
	public static final long cAtomQFNot4ENeighbours = 0x0000080000000000L;
	public static final long cAtomQFStereoState     = 0x0000300000000000L;
	public static final long cAtomQFIsStereo        = 0x0000100000000000L;
	public static final long cAtomQFIsNotStereo     = 0x0000200000000000L;
	public static final long cAtomQFHeteroAromatic  = 0x0000400000000000L;

	public static final int cBondTypeSingle			= 0x00000001;
	public static final int cBondTypeDouble			= 0x00000002;
	public static final int cBondTypeTriple			= 0x00000004;
	public static final int cBondTypeQuadruple		= 0x00000008;
	public static final int cBondTypeQuintuple		= 0x00000010;
	public static final int cBondTypeMetalLigand	= 0x00000020;
	public static final int cBondTypeDelocalized	= 0x00000040;
	public static final int cBondTypeDown			= 0x00000081;
	public static final int cBondTypeUp				= 0x00000101;
	public static final int cBondTypeCross			= 0x00000182;
	public static final int cBondTypeDeleted		= 0x00000200;
	public static final int cBondTypeIncreaseOrder  = 0x000001FF;

	public static final int cBondTypeMaskSimple 	= 0x0000007F;	// masks
	public static final int cBondTypeMaskStereo	    = 0x00000180;

	protected static final int cBondFlagsHelper2	= 0x000002C0;
	protected static final int cBondFlagsHelper3	= 0x0000003F;

		// double bond E/Z parities based on atom positions in atom table
	protected static final int cBondFlagsParity		= 0x00000003;
	public static final int cBondParityNone			= 0x00000000;
	public static final int cBondParityEor1			= 0x00000001;
	public static final int cBondParityZor2			= 0x00000002;
	public static final int cBondParityUnknown		= 0x00000003;
	private static final int cBondParityIsPseudo	= 0x00000004;

	private static final int cBondFlagsCIPParity	= 0x00000030;
	protected static final int cBondFlagsCIPParityShift = 4;
	public static final int cBondCIPParityNone		= 0x00000000;
	public static final int cBondCIPParityEorP		= 0x00000001;
	public static final int cBondCIPParityZorM		= 0x00000002;
	public static final int cBondCIPParityProblem   = 0x00000003;

	protected static final int cBondFlagRing		= 0x00000040;
	protected static final int cBondFlagSmallRing	= 0x00000080;
	protected static final int cBondFlagsESR		= 0x00007F00;
	private static final int cBondFlagsESRType		= 0x00000300;
	private static final int cBondFlagsESRTypeShift = 8;
	private static final int cBondFlagsESRGroup		= 0x00007C00;
	private static final int cBondFlagsESRGroupShift = 10;
	private static final int cBondFlagBGHilited		= 0x00008000;
	private static final int cBondFlagFGHilited		= 0x00010000;

	private static final int cBondParityUnknownOrNone = 0x0020000;
	// This hint/flag is set by CoordinateInventor for double bonds without
	// given EZ-parity because coordinates may imply a not intended EZ-parity.
	// The setBondParity() method clears this flag. The Canonizer considers
	// this flag when calculating EZ-parities.

	public static final int cBondQFNoOfBits			= 23;
	public static final int cBondQFBondTypesBits	= 5;
	public static final int cBondQFBondTypesShift	= 0;
	public static final int cBondQFRareBondTypesBits = 2;
	public static final int cBondQFRareBondTypesShift = 5;
	public static final int cBondQFRingStateBits	= 2;
	public static final int cBondQFRingStateShift	= 7;
	public static final int cBondQFBridgeBits		= 8;
	public static final int cBondQFBridgeShift		= 9;
	public static final int cBondQFBridgeMinBits	= 4;
	public static final int cBondQFBridgeMinShift   = 9;
	public static final int cBondQFBridgeSpanBits   = 4;
	public static final int cBondQFBridgeSpanShift  = 13;
	public static final int cBondQFRingSizeBits		= 3;
	public static final int cBondQFRingSizeShift	= 17;
	public static final int cBondQFAromStateBits	= 2;
	public static final int cBondQFAromStateShift	= 21;
	public static final int cBondQFAllFeatures		= 0x00FFFFFF;
	public static final int cBondQFSimpleFeatures	= 0x006001FF;
	public static final int cBondQFNarrowing		= 0x00600180;
	public static final int cBondQFBondTypes		= 0x0000001F;   // original 5 bond types for idcode
	public static final int cBondQFRareBondTypes    = 0x00000060;   // using OR logic for all 7 bond types
	public static final int cBondQFSingle           = 0x00000001;
	public static final int cBondQFDouble           = 0x00000002;
	public static final int cBondQFTriple           = 0x00000004;
	public static final int cBondQFDelocalized      = 0x00000008;
	public static final int cBondQFMetalLigand      = 0x00000010;
	public static final int cBondQFQuadruple        = 0x00000020;
	public static final int cBondQFQuintuple        = 0x00000040;
	public static final int cBondQFRingState		= 0x00000180;
	public static final int cBondQFNotRing			= 0x00000080;
	public static final int cBondQFRing				= 0x00000100;
	public static final int cBondQFBridge			= 0x0001FE00;
	public static final int cBondQFBridgeMin		= 0x00001E00;
	public static final int cBondQFBridgeSpan		= 0x0001E000;
	public static final int cBondQFRingSize			= 0x000E0000;
	public static final int cBondQFMatchStereo		= 0x00100000;
	public static final int cBondQFAromState		= 0x00600000;
	public static final int cBondQFAromatic			= 0x00200000;
	public static final int cBondQFNotAromatic		= 0x00400000;
	public static final int cBondQFMatchFormalOrder = 0x00800000; // matches the formal bond order considering also cBondQFBondTypes in query

	public static final int cHelperAll				= 0x00FF;
	public static final int cHelperNone				= 0x0000;
	public static final int cHelperBitNeighbours	= 0x0001;
	public static final int cHelperBitRingsSimple	= 0x0002;	// small rings only, no aromaticity, no allylic nor stabilized flags
	public static final int cHelperBitRings			= 0x0004;
	public static final int cHelperBitParities		= 0x0008;
	public static final int cHelperBitCIP			= 0x0010;

	public static final int cHelperBitSymmetrySimple			= 0x0020;
	public static final int cHelperBitSymmetryStereoHeterotopicity = 0x0040;
	public static final int cHelperBitIncludeNitrogenParities	= 0x0080;

	public static final int cHelperBitsStereo = 0x00F8;

	public static final int cHelperNeighbours = cHelperBitNeighbours;
	public static final int cHelperRingsSimple = cHelperNeighbours | cHelperBitRingsSimple;
	public static final int cHelperRings = cHelperRingsSimple | cHelperBitRings;
	public static final int cHelperParities = cHelperRings | cHelperBitParities;
	public static final int cHelperCIP = cHelperParities | cHelperBitCIP;

	public static final int cHelperSymmetrySimple = cHelperCIP | cHelperBitSymmetrySimple;
	public static final int cHelperSymmetryStereoHeterotopicity = cHelperCIP | cHelperBitSymmetryStereoHeterotopicity;

	public static final int cChiralityIsomerCountMask   = 0x00FFFF;
	public static final int cChiralityUnknown		  	= 0x000000;
	public static final int cChiralityNotChiral			= 0x010000;
	public static final int cChiralityMeso				= 0x020000; // this has added the number of meso isomers
	public static final int cChiralityRacemic			= 0x030000;
	public static final int cChiralityKnownEnantiomer   = 0x040000;
	public static final int cChiralityUnknownEnantiomer = 0x050000;
	public static final int cChiralityEpimers		 	= 0x060000;
	public static final int cChiralityDiastereomers		= 0x070000; // this has added the number of diastereomers

	public static final double cDefaultAVBL = 24.0;
	private static double sDefaultAVBL = cDefaultAVBL;

	public static final int cMoleculeColorDefault = 0;
	public static final int cMoleculeColorNeutral = 1;

	public static final int cPseudoAtomsHydrogenIsotops = 1;    // D and T
	public static final int cPseudoAtomsRGroups = 2;            // R1 to R16
	public static final int cPseudoAtomsAGroups = 4;            // A1,A2,A3
	public static final int cPseudoAtomR = 8;                   // R
	public static final int cPseudoAtomA = 16;                  // A
	public static final int cPseudoAtomX = 32;                  // X
	public static final int cPseudoAtomsAminoAcids = 64;        // all 20 amino acid 3-letter codes
	public static final int cPseudoAtomPolymer = 128;           // Pol
	public static final int cPseudoAtomAttachmentPoint = 256;   // ?
	public static final int cPseudoAtomsAll = 511;              // all of above

	public static final int cDefaultAllowedPseudoAtoms = cPseudoAtomsHydrogenIsotops
													   | cPseudoAtomsAminoAcids
													   | cPseudoAtomAttachmentPoint;

	public static final String[] cAtomLabel = { "?",
		"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" ,"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"  ,"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" ,"Hg" ,
		"Tl" ,"Pb" ,"Bi" ,"Po" ,"At" ,"Rn" ,"Fr" ,"Ra" ,
		"Ac" ,"Th" ,"Pa" ,"U"  ,"Np" ,"Pu" ,"Am" ,"Cm" ,
		"Bk" ,"Cf" ,"Es" ,"Fm" ,"Md" ,"No" ,"Lr" ,"Rf" ,
		"Db" ,"Sg" ,"Bh" ,"Hs" ,"Mt" ,"Ds" ,"Rg" ,"Cn" ,
		"Nh" ,"Fl" ,"Mc" ,"Lv" ,"Ts" ,"Og" ,"??" ,"??" ,
		"??" ,"??" ,"??" ,"??" ,"??" ,"??" ,"??" ,"??" ,
		"R4" ,"R5" ,"R6" ,"R7" ,"R8" ,"R9" ,"R10","R11",	// R4 to R16 do not belong to the MDL set
		"R12","R13","R14","R15","R16","R1" ,"R2" ,"R3" ,
		"A"  ,"A1" ,"A2" ,"A3" ,"??" ,"??" ,"D"  ,"T"  ,
		"X"  ,"R"  ,"H2" ,"H+" ,"Nnn","HYD","Pol","??" ,
		"??" ,"??" ,"??" ,"??" ,"??" ,"??" ,"??" ,"??" ,
		"??" ,"??" ,"Ala","Arg","Asn","Asp","Cys","Gln",
		"Glu","Gly","His","Ile","Leu","Lys","Met","Phe",
		"Pro","Ser","Thr","Trp","Tyr","Val" };

	public static final short[] cRoundedMass = { 0,
	   1,	  4,	  7,	  9,	 11,	 12,   //  H  ,He ,Li ,Be ,B  ,C  ,
	  14,	 16,	 19,	 20,	 23,	 24,   //  N , O  ,F  ,Ne ,Na ,Mg ,
	  27,	 28,	 31,	 32,	 35,	 40,   //  Al ,Si ,P  ,S  ,Cl ,Ar ,
	  39,	 40,	 45,	 48,	 51,	 52,   //  K  ,Ca ,Sc ,Ti ,V  ,Cr ,
	  55,	 56,	 59,	 58,	 63,	 64,   //  Mn ,Fe ,Co ,Ni ,Cu ,Zn ,
	  69,	 74,	 75,	 80,	 79,	 84,   //  Ga ,Ge ,As ,Se ,Br ,Kr ,
	  85,	 88,	 89,	 90,	 93,	 98,   //  Rb ,Sr ,Y  ,Zr ,Nb ,Mo ,
	   0,	102,	103,	106,	107,	114,   //  Tc ,Ru ,Rh ,Pd ,Ag ,Cd ,
	 115,	120,	121,	130,	127,	132,   //  In ,Sn ,Sb ,Te ,I  ,Xe ,
	 133,	138,	139,	140,	141,	142,   //  Cs ,Ba ,La ,Ce ,Pr ,Nd ,
	   0,	152,	153,	158,	159,	164,   //  Pm ,Sm ,Eu ,Gd ,Tb ,Dy ,
	 165,	166,	169,	174,	175,	180,   //  Ho ,Er ,Tm ,Yb ,Lu ,Hf ,
	 181,	184,	187,	192,	193,	195,   //  Ta ,W , Re ,Os ,Ir ,Pt ,
	 197,	202,	205,	208,	209,	209,   //  Au ,Hg ,Tl ,Pb ,Bi ,Po ,
	 210,	222,	223,	226,	227,	232,   //  At ,Rn ,Fr ,Ra ,Ac ,Th ,
	 231,	238,	237,	244,	243,	247,   //  Pa ,U , Np ,Pu ,Am ,Cm ,
	 247,	251,	252,	257,	258,	259,   //  Bk ,Cf ,Es ,Fm ,Md ,No ,
	 262,	267,	268,	271,	270,	277,   //  Lr ,Rf ,Db ,Sg ,Bh ,Hs ,
	 276,	281,	281,	283,	285,	289,   //  Mt ,Ds ,Rg ,Cn ,Nh ,Fl ,
	 289,	293,	294,	294,	  0,	  0,   //  Mc ,Lv ,Ts ,Og ,?? ,?? ,
	   0,	  0,	  0,	  0,	  0,	  0,   //  ?? ,?? ,?? ,?? ,?? ,?? ,
	   0,	  0,	  0,	  0,	  0,	  0,   //  ?? ,?? ,R4 ,R5 ,R6 ,R7 ,
	   0,	  0,	  0,	  0,	  0,	  0,   //  R8 ,R9 ,R10,R11,R12,R13,
	   0,	  0,	  0,	  0,	  0,	  0,   //  R14,R15,R16,R1 ,R2 ,R3 ,
	   0,	  0,	  0,	  0,	  0,	  0,   //  A  ,A1 ,A2 ,A3 ,?? ,?? ,
	   2,	  3,	  0,	  0,	  0,	  0,   //  D  ,T  ,X  ,R  ,H2 ,H+
	   0,	  0,	  0,	  0,	  0,	  0,   //  Nnn,HYD,Pol,?? ,?? ,?? ,
	   0,	  0,	  0,	  0,	  0,	  0,   //  ?? ,?? ,?? ,?? ,?? ,?? ,
	   0,	  0,	 71,	156,	114,	115,   //  ?? ,?? ,Ala,Arg,Asn,Asp,
	 103,	128,	129,	 57,	137,	113,   //  Cys,Gln,Glu,Gly,His,Ile,
	 113,	128,	131,	147,	 97,	 87,   //  Leu,Lys,Met,Phe,Pro,Ser,
	 101,	186,	163,	 99 };					//  Thr,Trp,Tyr,Val,

	public static final int cDefaultAtomValence = 6;
	private static final byte[] cDefaultAtomValences = { cDefaultAtomValence };
	private static final byte[] cAminoAcidValences = { 2 };
	public static final byte[][] cAtomValence = {null,
			{1}, {0}, {1}, {2}, {3}, {4}, {3}, {2}, {1}, {0},			// H to Ne
			{1}, {2}, {3}, {4}, {3, 5}, {2, 4, 6}, {1, 3, 5, 7}, {0},	// Na to Ar
			{1}, {2}, null, null, null, null, null, null, null, null,	// K to Ni
			null, null, {2, 3}, {2, 4}, {3, 5}, {2, 4, 6}, {1, 3, 5, 7}, {0, 2}, // Cu to Kr
			{1}, {2}, null, null, null, null, null, null, null, null,	// Rb to Pd
			null, null, {1, 2, 3}, {2, 4}, {3, 5}, {2, 4, 6}, {1, 3, 5, 7}, // Ag to I
			{0, 2, 4, 6}, {1}, {2},										// Xe to Ba
			null, null, null, null, null, null, null, null, null, null, // La to Dy
			null, null, null, null, null, null, null, null, null, null, // Ho to Os
			null, null, null, null, null, null, null, null, null, null, // Ir to Rn
			null, null, null, null, null, null, null, null, null, null, // Fr to Cm
			null, null, null, null, null, null, null, null, null, null, // Bk to Sg
			null, null, null, null, null, null, null, null, null, null, // Bh to Lv
			null, null, null, null, null, null, null, null, null, null, // Ts to 126
			null, null, null, null, null, null, null, null, null, null,	// 127 to R5
			null, null, null, null, null, null, null, null, null, null, // R6 to R15
			null, null, null, null, null, null, null, null, null, null,	// R16 to 156
			null, null, null, null, null, null, null, null, null, null, // D to 166
			null, null, null, null,										// 167 to 170
			{2}, {2}, {2}, {2}, {3}, {2}, {2}, {2}, {2}, {2},			// Ala to Ile
			{2}, {2}, {2}, {2}, {2}, {2}, {2}, {2}, {2}, {2},			// Leu to Val
	};

	// Taken from http://www.cabrillo.edu/~aromero/Common%20Files/Periodic%20Table%20(Common%20Ionic%20Charges).pdf
	public static final byte[][] cCommonOxidationState = { null,
			{1}, null, {1}, {2}, null, null,					//  H,  He, Li, Be, B,  C,
			{-3}, {-2}, {-1}, null, {1}, {2},					//  N,  O,  F,  Ne, Na, Mg,
			{3}, null, {-3}, {-2}, {-1}, null,					//  Al, Si, P,  S,  Cl, Ar,
			{1}, {2}, {3}, {2,3,4}, {2,3,4,5}, {2,3,6},			//  K,  Ca, Sc, Ti, V,  Cr,
			{2,3,4,7}, {2,3}, {2,3}, {2,3}, {1, 2}, {2},		//  Mn, Fe, Co, Ni, Cu, Zn,
			{3}, {2, 4}, {-3,3,5}, {-2}, {-1}, null,			//  Ga, Ge, As, Se, Br, Kr,
			{1}, {2}, {3}, {4}, {3,5}, {6},						//  Rb, Sr, Y,  Zr, Nb, Mo,
			{4,6,7}, {3}, {3}, {2,4}, {1}, {2},					//  Tc, Ru, Rh, Pd, Ag, Cd,
			{3}, {2,4}, {-3,3,5}, {-2,4,6}, {-1}, null,			//  In, Sn, Sb, Te, I,  Xe,
			{1}, {2}, {3}, {3,4}, {3}, {3},						//  Cs, Ba, La ,Ce, Pr, Nd,
			{3}, {2,3}, {2,3}, {3}, {3}, {3},					//  Pm, Sm, Eu, Gd, Tb, Dy,
			{3}, {3}, {3}, {2,3}, {3}, {4},						//  Ho, Er, Tm, Yb, Lu, Hf,
			{5}, {6}, {4,6,7}, {3, 4}, {3,4}, {2,4},			//  Ta, W,  Re, Os, Ir, Pt,
			{1,3}, {1,2}, {1,3}, {2,4}, {3,5}, {-2,2,4},		//  Au, Hg, Tl, Pb, Bi, Po,
			{-1,1}, null, {1}, {2}, {3}, {4},					//  At, Rn, Fr, Ra, Ac, Th,
			{4,5}, {3,4,5,6}, {3,4,5,6}, {3,4,5,6}, {3,4,5,6},	//  Pa, U,  Np, Pu, Am,
			{3}, {3,4}, {3}, {3}, {3}, {2,3},					//  Cm, Bk, Cf, Es, Fm, Md,
			{2,3}, {3}											//  No, Lr
		};

	transient protected int mMaxAtoms;
	transient protected int mMaxBonds;

	transient protected int mValidHelperArrays;
	transient protected int mAllAtoms;
	transient protected int mAllBonds;
	transient protected int[] mAtomicNo;
	transient protected int[] mAtomCharge;
	transient protected int[] mAtomMapNo;
	transient protected int[] mAtomMass;
	transient protected int[] mAtomFlags;
	transient protected long[] mAtomQueryFeatures;
	transient protected int[][] mBondAtom;
	transient protected int[] mBondType;
	transient protected int[] mBondFlags;
	transient protected int[] mBondQueryFeatures;
	transient protected Coordinates[] mCoordinates;
	transient protected boolean mIsFragment;
	transient protected boolean mIsRacemate;	 	// to indicate a molfileV2's chiral flat to be 0
	transient protected boolean mProtectHydrogen;  	// protects hydrogens atoms from being converted to query features
	transient protected int mChirality;			// property set by Canonizer
	transient protected int[][] mAtomList;
	transient protected byte[][] mAtomCustomLabel;

	transient private int mMoleculeColor;
	transient private double mZoomRotationX,mZoomRotationY;
	transient private double[] mOriginalAngle;
	transient private double[] mOriginalDistance;
	transient private String mName;
	transient private Object mUserData;

    public static int getAtomicNoFromLabel(String atomLabel) {
	    return getAtomicNoFromLabel(atomLabel, cDefaultAllowedPseudoAtoms);
		}

	public static int getAtomicNoFromLabel(String atomLabel, int allowedPseudoAtomGroups) {
    	if (((allowedPseudoAtomGroups & cPseudoAtomAttachmentPoint) != 0) && atomLabel.equals("?"))
			return 0;

		for (int i=1; i<=128; i++)
			if (!atomLabel.equals("??") && atomLabel.equalsIgnoreCase(cAtomLabel[i]))
				return i;

		if ((allowedPseudoAtomGroups & cPseudoAtomsRGroups) != 0)
			for (int i=129; i<=144; i++)
				if (atomLabel.equalsIgnoreCase(cAtomLabel[i]))
					return i;

		if ((allowedPseudoAtomGroups & cPseudoAtomsAGroups) != 0)
			for (int i=146; i<=148; i++)
				if (atomLabel.equalsIgnoreCase(cAtomLabel[i]))
					return i;

		if ((allowedPseudoAtomGroups & cPseudoAtomsHydrogenIsotops) != 0)
			for (int i=151; i<=152; i++)
				if (atomLabel.equalsIgnoreCase(cAtomLabel[i]))
					return i;

		if ((allowedPseudoAtomGroups & cPseudoAtomX) != 0)
			if (atomLabel.equalsIgnoreCase(cAtomLabel[153]))
				return 153;

		if ((allowedPseudoAtomGroups & cPseudoAtomR) != 0)
			if (atomLabel.equalsIgnoreCase(cAtomLabel[154]))
				return 154;

		if ((allowedPseudoAtomGroups & cPseudoAtomA) != 0)
			if (atomLabel.equalsIgnoreCase(cAtomLabel[145]))
				return 145;

		if ((allowedPseudoAtomGroups & cPseudoAtomPolymer) != 0)
			if (atomLabel.equalsIgnoreCase(cAtomLabel[159]))
				return 159;

		if ((allowedPseudoAtomGroups & cPseudoAtomsAminoAcids) != 0)
			for (int i=171; i<=190; i++)
				if (atomLabel.equalsIgnoreCase(cAtomLabel[i]))
					return i;

		return 0;
		}

	/**
	 * For any known atomicNo this returns all allowed atom valences.
	 * For amino acid pseudo atoms it returns {2} and for all other atomicNos
	 * this returns {cDefaultAtomValence}.
	 * @param atomicNo
	 * @return array of allowed valences with a guaranteed minimum size of 1
	 */
	public static byte[] getAllowedValences(int atomicNo) {
    	return (atomicNo >= 0)
		    && (atomicNo < cAtomValence.length)
		    && (cAtomValence[atomicNo] != null) ? cAtomValence[atomicNo]
			: (atomicNo >= 171 && atomicNo <= 190) ? cAminoAcidValences : cDefaultAtomValences;
		}

	/**
	 * Calculates the angle of the line leading from (x1,y1) to (x2,y2).
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return -Pi to Pi
	 */
	public static double getAngle(double x1, double y1, double x2, double y2) {
		double angle;
		double xdiff = x2 - x1;
		double ydiff = y2 - y1;

		if (ydiff != 0) {
			angle = Math.atan(xdiff/ydiff);
			if (ydiff < 0) {
				if (xdiff < 0)
					angle -= Math.PI;
				else
					angle += Math.PI;
				}
			}
		else
			angle = (xdiff > 0f) ? Math.PI/2 : -Math.PI/2;

		return angle;
		}


	/**
	 * Calculates the difference of two angles as angle1-angle2
	 * @param angle1
	 * @param angle2
	 * @return -Pi to Pi
	 */
	public static double getAngleDif(double angle1, double angle2) {
		double angleDif = angle1 - angle2;
		while (angleDif < -Math.PI)
			angleDif += 2 * Math.PI;
		while (angleDif > Math.PI)
			angleDif -= 2 * Math.PI;
		return angleDif;
		}


	public static int bondTypeToOrder(int bondType) {
		int simpleType = bondType & cBondTypeMaskSimple;
		return (simpleType == cBondTypeSingle
			 || simpleType == cBondTypeDelocalized) ? 1
			  : simpleType == cBondTypeDouble ? 2
			  : simpleType == cBondTypeTriple ? 3
			  : simpleType == cBondTypeQuadruple ? 4
			  : simpleType == cBondTypeQuintuple ? 5 : 0; // dative bonds
		}


	public static int bondOrderToType(int bondOrder, boolean useCrossBond) {
		return bondOrder == 0 ? Molecule.cBondTypeMetalLigand
			 : bondOrder == 1 ? Molecule.cBondTypeSingle
			 : bondOrder == 2 ? (useCrossBond ? Molecule.cBondTypeCross : Molecule.cBondTypeDouble)
			 : bondOrder == 3 ? Molecule.cBondTypeTriple
			 : bondOrder == 4 ? Molecule.cBondTypeQuadruple
							  : Molecule.cBondTypeQuintuple;
		}


	public Molecule() {
		mMaxAtoms = mMaxBonds = 256;
		init();
		}


	public Molecule(int maxAtoms, int maxBonds) {
		mMaxAtoms = Math.max(1, maxAtoms);
		mMaxBonds = Math.max(1, maxBonds);
		init();
		}


	private void init() {
		mValidHelperArrays = cHelperNone;
		mAtomicNo = new int[mMaxAtoms];
		mAtomCharge = new int[mMaxAtoms];
		mAtomMapNo = new int[mMaxAtoms];
		mCoordinates = new Coordinates[mMaxAtoms];
		for (int i=0; i= mMaxAtoms)
			setMaxAtoms(mMaxAtoms*2);

		mAtomicNo[mAllAtoms] = 0;			// default
		setAtomicNo(mAllAtoms, atomicNo);	// sets atomicNo and mass

		mAtomCharge[mAllAtoms] = 0;
		mAtomFlags[mAllAtoms] = 0;
		mAtomQueryFeatures[mAllAtoms] = 0;
		mAtomMapNo[mAllAtoms] = 0;
		mCoordinates[mAllAtoms].set(0, 0, 0);

		if (mAtomList != null)
			mAtomList[mAllAtoms] = null;
		if (mAtomCustomLabel != null)
			mAtomCustomLabel[mAllAtoms] = null;

		mValidHelperArrays = cHelperNone;
		return mAllAtoms++;
		}


	/**
	 * Suggests either cBondTypeSingle or cBondTypeMetalLigand
	 * whatever seems more appropriate for a new bond between the two atoms.
	 * @param atom1
	 * @param atom2
	 * @return preferred bond type
	 */
	public int suggestBondType(int atom1, int atom2) {
		return isMetalAtom(atom1) || isMetalAtom(atom2) ?
				cBondTypeMetalLigand : cBondTypeSingle;
	}

	/**
	 * High level function for constructing a molecule.
	 * Adds a single or metal bond between the two atoms
	 * depending on whether one of them is a metal atom.
	 * @param atom1
	 * @param atom2
	 * @return new bond index
	 */
	public int addBond(int atom1, int atom2) {
		return addBond(atom1, atom2, suggestBondType(atom1, atom2));
		}


	/**
	 * High level function for constructing a molecule.
	 * @param atom1
	 * @param atom2
	 * @param type
	 * @return new bond index
	 */
	public int addBond(int atom1, int atom2, int type) {
		if (atom1 == atom2)
			return -1;

		for (int bnd=0; bnd= mMaxBonds)
			setMaxBonds(mMaxBonds*2);

		mBondAtom[0][mAllBonds] = atom1;
		mBondAtom[1][mAllBonds] = atom2;
		mBondType[mAllBonds] = type;
		mBondFlags[mAllBonds] = 0;
		mBondQueryFeatures[mAllBonds] = 0;
		mValidHelperArrays = cHelperNone;
//		checkAtomParity(atm1);
//		checkAtomParity(atm2);
		return mAllBonds++;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param x
	 * @param y
	 * @param atomicNo
	 * @param mass
	 * @param abnormalValence
	 * @param radical
	 * @return
	 */
	public boolean addOrChangeAtom(double x, double y, int atomicNo, int mass, int abnormalValence, int radical, String customLabel) {
		int atom = findAtom(x,y);
		if (atom == -1) {
			if (mAllAtoms >= mMaxAtoms)
				setMaxAtoms(mMaxAtoms*2);

			atom = addAtom(atomicNo);
			mCoordinates[atom].set(x, y, 0);
			mAtomMass[atom] = mass;
			setAtomAbnormalValence(atom, abnormalValence);
			setAtomRadical(atom, radical);
			setAtomCustomLabel(atom, customLabel);
			return true;
			}

		boolean changed = changeAtom(atom, atomicNo, mass, abnormalValence, radical);
		setAtomCustomLabel(atom, customLabel);
		return changed;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param atm1
	 * @param atm2
	 * @param type
	 * @return
	 */
	public int addOrChangeBond(int atm1,int atm2,int type) {
		for (int bnd=0; bnd= mMaxBonds)
			setMaxBonds(mMaxBonds*2);

		mBondAtom[0][mAllBonds] = atm1;
		mBondAtom[1][mAllBonds] = atm2;
		mBondType[mAllBonds] = type;
		mBondFlags[mAllBonds] = 0;
		mBondQueryFeatures[mAllBonds] = 0;
		mValidHelperArrays = cHelperNone;
		return mAllBonds++;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param x
	 * @param y
	 * @param ringSize
	 * @param aromatic
	 * @return
	 */
	public boolean addRing(double x, double y, int ringSize, boolean aromatic, double bondLength) {
		while(mAllAtoms + ringSize > mMaxAtoms)
			setMaxAtoms(mMaxAtoms*2);
		while(mAllBonds + ringSize > mMaxBonds)
			setMaxBonds(mMaxBonds*2);

		int atom = findAtom(x,y);
		if (atom != -1)
			return addRingToAtom(atom, ringSize, aromatic, bondLength);

		int bond = findBond(x,y);
		if (bond != -1)
			return addRingToBond(bond, ringSize, aromatic, bondLength);

		// new ring in empty space
		atom = addAtom(x,y);
		double cornerAngle = Math.PI * (ringSize-2)/ringSize;
		polygon(atom, ringSize, atom,aromatic, 0, Math.PI - cornerAngle, bondLength);
		mValidHelperArrays = cHelperNone;
		return true;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param atom
	 * @param ringSize
	 * @param aromatic
	 * @return
	 */
	public boolean addRingToAtom(int atom, int ringSize, boolean aromatic, double bondLength) {
		if ((aromatic && getOccupiedValence(atom) > 1)
		 || (!aromatic && getOccupiedValence(atom) > 2))
			return false;

		int angles = 0;
		double[] angle = new double[4];
		for (int i=0; i Math.PI) ? (angle[0] + angle[1])/2
				: (angle[0] + angle[1])/2 + Math.PI;

		double cornerAngle = (Math.PI * (ringSize-2))/ringSize;
		polygon(atom, ringSize, atom, aromatic, newAngle-cornerAngle/2, Math.PI - cornerAngle, bondLength);
		mValidHelperArrays = cHelperNone;
//				checkAtomParity(atom);
		return true;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param bond
	 * @param ringSize
	 * @param aromatic
	 * @return
	 */
	public boolean addRingToBond(int bond, int ringSize, boolean aromatic, double bondLength) {
		int[] bondAtom = new int[2];
		double[] bondAngle = new double[2];

		bondAtom[0] = mBondAtom[0][bond];
		bondAtom[1] = mBondAtom[1][bond];
		if (getOccupiedValence(bondAtom[0]) > 3) return false;
		if (getOccupiedValence(bondAtom[1]) > 3) return false;
		int angles = 0;
		double[] angle = new double[4];
		for (int i=0; i bondAngle[0]) && (angle[i] < bondAngle[1]))
				side--;
			else
				side++;
			}

		atomNo = (side > 0) ? 1-atomNo : atomNo;
		double cornerAngle = (Math.PI * (ringSize-2))/ringSize;
		polygon(bondAtom[atomNo], ringSize-1,
				bondAtom[1-atomNo], aromatic,
				bondAngle[(side > 0) ? 0 : 1] + Math.PI - cornerAngle, Math.PI - cornerAngle, bondLength);

		mValidHelperArrays = cHelperNone;
//		checkAtomParity(bondAtom[0]);
//		checkAtomParity(bondAtom[1]);
		return true;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param atom
	 * @param atomicNo
	 * @param mass
	 * @param abnormalValence
	 * @param radical
	 * @return
	 */
	public boolean changeAtom(int atom, int atomicNo, int mass, int abnormalValence, int radical) {
		if ((atomicNo == 1 || atomicNo == 151 || atomicNo == 152)
		 && getOccupiedValence(atom) > 1)
			return false;

		mAtomQueryFeatures[atom] &= ~cAtomQFAny;
		if (mAtomList != null)
			mAtomList[atom] = null;
		if (mAtomCustomLabel != null)
			mAtomCustomLabel[atom] = null;

		if (atomicNo == mAtomicNo[atom]
		 && mass == mAtomMass[atom]
		 && abnormalValence == getAtomAbnormalValence(atom)
		 && radical == getAtomRadical(atom))
			return false;

		if (atomicNo == 151 || atomicNo == 152) {	// 'D' or 'T'
			mass = atomicNo - 149;
			atomicNo = 1;
			}

		mAtomFlags[atom] &= (cAtomFlagsColor | cAtomFlagSelected);
		mAtomicNo[atom] = atomicNo;
		mAtomMass[atom] = mass;
		mAtomCharge[atom] = 0;
		mAtomQueryFeatures[atom] = 0;
		setAtomAbnormalValence(atom, abnormalValence);
		setAtomRadical(atom, radical);
		removeMappingNo(mAtomMapNo[atom]);

		mValidHelperArrays = cHelperNone;
//		checkAtomParity(atom);
		return true;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param x
	 * @param y
	 * @param positive
	 * @return
	 */
	public boolean changeAtomCharge(double x, double y, boolean positive) {
		int atom = findAtom(x,y);
		return atom != -1 && changeAtomCharge(atom, positive);
		}


	/**
	 * High level function for constructing a molecule.
	 * @param atom
	 * @param positive
	 * @return
	 */
	public boolean changeAtomCharge(int atom, boolean positive) {
		if (positive) {
			if (mAtomCharge[atom] > 8) return false;
			mAtomCharge[atom]++;
			}
		else {
			if (mAtomCharge[atom] < -8) return false;
			mAtomCharge[atom]--;
			}
		mValidHelperArrays = cHelperNone;
//		checkAtomParity(atom);
		return true;
		}


	/**
	 * High level function for constructing a molecule.
	 * @param bnd
	 * @param type
	 * @return
	 */
	public boolean changeBond(int bnd, int type) {
		boolean bondWasChanged = false;
		int oldType = mBondType[bnd];

		if (type == cBondTypeIncreaseOrder) {
			bondWasChanged = incrementBondOrder(bnd);
			}
		else if (validateBondType(bnd, type)) {
			if (type == cBondTypeUp || type == cBondTypeDown) {
				boolean bondAtAtom1Qualifies = qualifiesAsStereoBond(bnd, mBondAtom[0][bnd]);
				boolean bondAtAtom2Qualifies = qualifiesAsStereoBond(bnd, mBondAtom[1][bnd]);
				if (type == oldType) {
					// If both atoms are stereocenters (or none is recognized yet as stereo center)
					// or if only one atom is a stereo center and the bond points to the other one
					// then we can invert the bond direction.
					if (bondAtAtom1Qualifies == bondAtAtom2Qualifies
					 || bondAtAtom2Qualifies) {
						int temp = mBondAtom[0][bnd];
						mBondAtom[0][bnd] = mBondAtom[1][bnd];
						mBondAtom[1][bnd] = temp;
						bondWasChanged = true;
						}
					}
				else {
						// if stereo center information available assure proper bond direction
					if (!bondAtAtom1Qualifies && bondAtAtom2Qualifies) {
						int temp = mBondAtom[0][bnd];
						mBondAtom[0][bnd] = mBondAtom[1][bnd];
						mBondAtom[1][bnd] = temp;
						}
					mBondType[bnd] = type;
					bondWasChanged = true;
					}
				}
			else {
				mBondType[bnd] = type;
				bondWasChanged = true;
				}
			}

		if (bondWasChanged) {
			mValidHelperArrays = (oldType & cBondTypeMaskSimple)
								 == (type & cBondTypeMaskSimple) ?
						mValidHelperArrays & cHelperRings
					  : cHelperNone;
			mBondQueryFeatures[bnd] = 0;
			}

		return bondWasChanged;
		}


	/**
	 * Checks whether this bond may rightfully be an up/down stereo bond with its pointed end
	 * connected to atom. 
	 * i.e. whether it is a stereo center, an allene end atom, or an atom of a BINAP bond.
	 * @return true if an attached stereo bond aids in stereo recognition
	 */
	private boolean qualifiesAsStereoBond(int bond, int atom) {
		if (getBondOrder(bond) != 1)
			return false;

		if ((mAtomFlags[atom] & cAtomFlagsParity) != cAtomParityNone)
			return true;

		// stereo bond at allene 
		for (int i=0; i= destMol.mMaxAtoms)
			destMol.setMaxAtoms(destMol.mMaxAtoms*2);

		int esrType = getAtomESRType(sourceAtom);
		int esrGroup = -1;
		if (esrType == cESRTypeAnd) {
			if (esrGroupOffsetAND == -1)   // create a new ESR group for this atom
				esrGroup = destMol.renumberESRGroups(esrType);
			else	// take existing group and add offset that should be the
					// ESR group member count of destMol before starting to add atoms
				esrGroup = Math.min(cESRMaxGroups-1, esrGroupOffsetAND + getAtomESRGroup(sourceAtom));
			}
		else if (esrType == cESRTypeOr) {
			if (esrGroupOffsetOR == -1)   // create a new ESR group for this atom
				esrGroup = destMol.renumberESRGroups(esrType);
			else	// take existing group and add offset that should be the
					// ESR group member count of destMol before starting to add atoms
				esrGroup = Math.min(cESRMaxGroups-1, esrGroupOffsetOR + getAtomESRGroup(sourceAtom));
			}

		destMol.mAtomicNo[destAtom] = mAtomicNo[sourceAtom];
		destMol.mAtomCharge[destAtom] = mAtomCharge[sourceAtom];
		destMol.mAtomMass[destAtom] = mAtomMass[sourceAtom];
		destMol.mAtomFlags[destAtom] = mAtomFlags[sourceAtom];
		destMol.mAtomQueryFeatures[destAtom] = destMol.mIsFragment ? mAtomQueryFeatures[sourceAtom] : 0;
		destMol.mCoordinates[destAtom].set(mCoordinates[sourceAtom]);
		destMol.mAtomMapNo[destAtom] = mAtomMapNo[sourceAtom];

		if (destMol.mAtomList != null)
			destMol.mAtomList[destAtom] = null;
		if (mAtomList != null && mAtomList[sourceAtom] != null && destMol.mIsFragment) {
			if (destMol.mAtomList == null)
				destMol.mAtomList = new int[destMol.mAtomicNo.length][];

			destMol.mAtomList[destAtom] = Arrays.copyOf(mAtomList[sourceAtom], mAtomList[sourceAtom].length);
			}

		if (destMol.mAtomCustomLabel != null)
			destMol.mAtomCustomLabel[destAtom] = null;
		if (mAtomCustomLabel != null && mAtomCustomLabel[sourceAtom] != null) {
			if (destMol.mAtomCustomLabel == null)
				destMol.mAtomCustomLabel = new byte[destMol.mAtomicNo.length][];

			destMol.mAtomCustomLabel[destAtom] = Arrays.copyOf(mAtomCustomLabel[sourceAtom], mAtomCustomLabel[sourceAtom].length);
			}

		if (esrGroup != -1) {
			destMol.mAtomFlags[destAtom] &= ~cAtomFlagsESRGroup;
			destMol.mAtomFlags[destAtom] |= (esrGroup << cAtomFlagsESRGroupShift);
			}

		destMol.mAllAtoms++;
		destMol.mValidHelperArrays = cHelperNone;

		return destAtom;
		}


	/**
	 * @param destMol
	 * @param sourceBond
	 * @param esrGroupOffsetAND -1 to create new ESR group or destMol ESR group count from esrGroupCountAND()
	 * @param esrGroupOffsetOR -1 to create new ESR group or destMol ESR group count from esrGroupCountOR()
	 * @param atomMap
	 * @param useBondTypeDelocalized
	 * @return
	 */
	public int copyBond(Molecule destMol, int sourceBond, int esrGroupOffsetAND, int esrGroupOffsetOR,
						int[] atomMap, boolean useBondTypeDelocalized) {
		return copyBond(destMol, sourceBond, esrGroupOffsetAND, esrGroupOffsetOR,
						(atomMap == null) ? mBondAtom[0][sourceBond] : atomMap[mBondAtom[0][sourceBond]],
						(atomMap == null) ? mBondAtom[1][sourceBond] : atomMap[mBondAtom[1][sourceBond]],
						useBondTypeDelocalized);
		}

	/**
	 * @param destMol
	 * @param sourceBond
	 * @param esrGroupOffsetAND -1 to create new ESR group or destMol ESR group count from esrGroupCountAND()
	 * @param esrGroupOffsetOR -1 to create new ESR group or destMol ESR group count from esrGroupCountOR()
	 * @param destAtom1 first bond atom index in destination molecule
	 * @param destAtom2 second bond atom index in destination molecule
	 * @param useBondTypeDelocalized
	 * @return
	 */
	public int copyBond(Molecule destMol, int sourceBond, int esrGroupOffsetAND, int esrGroupOffsetOR,
						int destAtom1, int destAtom2, boolean useBondTypeDelocalized) {
		int destBond = destMol.mAllBonds;
		if (destBond >= destMol.mMaxBonds)
			destMol.setMaxBonds(destMol.mMaxBonds * 2);

		int esrType = getBondESRType(sourceBond);
		int esrGroup = -1;
		if (esrType == cESRTypeAnd) {
			if (esrGroupOffsetAND == -1)   // create a new ESR group for this atom
				esrGroup = destMol.renumberESRGroups(esrType);
			else	// take existing group and add offset that should be the
					// ESR group member count of destMol before starting to add atoms
				esrGroup = Math.min(cESRMaxGroups, esrGroupOffsetAND + getBondESRGroup(sourceBond));
			}
		if (esrType == cESRTypeOr) {
			if (esrGroupOffsetOR == -1)   // create a new ESR group for this atom
				esrGroup = destMol.renumberESRGroups(esrType);
			else	// take existing group and add offset that should be the
					// ESR group member count of destMol before starting to add atoms
				esrGroup = Math.min(cESRMaxGroups, esrGroupOffsetOR + getBondESRGroup(sourceBond));
			}

		destMol.mBondAtom[0][destBond] = destAtom1;
		destMol.mBondAtom[1][destBond] = destAtom2;

		int bondType = (useBondTypeDelocalized && isDelocalizedBond(sourceBond)) ?
									cBondTypeDelocalized : mBondType[sourceBond];
		destMol.mBondType[destBond] = bondType;
		destMol.mBondFlags[destBond] = mBondFlags[sourceBond];
		destMol.mBondQueryFeatures[destBond] = destMol.mIsFragment ? mBondQueryFeatures[sourceBond] : 0;

		if (esrGroup != -1) {
			destMol.mBondFlags[destBond] &= ~cBondFlagsESRGroup;
			destMol.mBondFlags[destBond] |= (esrGroup << cBondFlagsESRGroupShift);
			}

		destMol.mAllBonds++;
		destMol.mValidHelperArrays = cHelperNone;

		return destBond;
		}


	/**
	 * Copies name,isFragment,chirality and validity of parity & CIP flags.
	 * When copying molecules parts only or when changing the atom order during copy,
	 * then atom parities or CIP parities may not be valid anymore and
	 * invalidateHelperArrays([affected bits]) should be called in these cases.
	 * @param destMol
	 */
	public void copyMoleculeProperties(Molecule destMol) {
		destMol.mIsFragment = mIsFragment;
		destMol.mIsRacemate = mIsRacemate;
		destMol.mProtectHydrogen = mProtectHydrogen;
		destMol.mChirality = mChirality;
		destMol.mName = mName;
		destMol.mValidHelperArrays = (mValidHelperArrays & (cHelperBitParities | cHelperBitCIP));
		}

	/**
	 * Clears helperBits from mValidHelperArrays.
	 * @param helperBits
	 */
	public void invalidateHelperArrays(int helperBits) {
		mValidHelperArrays &= ~helperBits;
		}


	/**
	 * For the given ESR type (AND or OR) renumbers all group indexes starting from 0.
	 * Use this, if stereo center deletion or other operations caused an inconsisten ESR
	 * number state. Molecule and derived methods do this automatically.
	 * @param type cESRTypeAnd or cESRTypeOr
	 * @return number of ESR groups
	 */
	public int renumberESRGroups(int type) {
		if (type == cESRTypeAbs)
			return 0;

		// reassign group numbers from bottom up
		boolean[] groupUsed = null;
		for (int atom=0; atom
	 * When multiple atoms and/or bonds need to be deleted, marking them and calling
	 * this method is more efficient than deleting them individually with deleteAtom() and
	 * deleteBond().
	 * Bonds, whose atoms carry opposite charges are treated in the following manner: If only one of
	 * the two bond atoms is kept, then its absolute charge will be reduced by 1.
	 * After the deletion the original order of atom and bond indexes is retained.
	 * @return mapping from old to new atom indices; null if no atoms nor bonds were deleted
	 */
	public int[] deleteMarkedAtomsAndBonds() {
		boolean found = false;
		for (int atom=0; atom bondLength / 2.0) continue;

			double distance;
			if (x2 == x1)// if pick posn closer to bond than cPickRange return bnd
				distance = Math.abs(x1 - pickx);
			else {
				double constA = (y2-y1)/(x1-x2);
				double constC = - constA * x1 - y1;
				distance = Math.abs((constA * pickx + picky + constC)
						 / Math.sqrt(constA * constA + 1));
				}
			if (distance < maxDistance
			 && distance < foundDistance) {
				foundDistance = distance;
				foundBond = bond;
				}
			}
		return foundBond;
		}


	/**
	 * @return the number of all atoms, which includes hydrogen atoms.
	 */
	public int getAllAtoms() {
		return mAllAtoms;
		}


	/**
	 * @return the number of all bonds, which includes those connecting hydrogen atoms.
	 */
	public int getAllBonds() {
		return mAllBonds;
		}


	/**
	 * Get an atom's defined maximum valance if different from the default one.
	 * @param atom
	 * @return valence 0-14: new maximum valence; -1: use default
	 */
	public int getAtomAbnormalValence(int atom) {
		return ((mAtomFlags[atom] & cAtomFlagsValence) >>> cAtomFlagsValenceShift) -1;
		}


	/**
	 * @param atom
	 * @return the formal atom charge
	 */
	public int getAtomCharge(int atom) {
		return mAtomCharge[atom];
		}


	/**
	 * The atom Cahn-Ingold-Prelog parity is a calculated property available above/equal helper level cHelperCIP.
	 * It encodes the stereo configuration of an atom with its neighbors using up/down-bonds
	 * or 3D-atom-coordinates, whatever is available. It depends on the atom indices of the neighbor
	 * atoms and their orientation is space. This method is called by the Canonizer and usually should not
	 * be called otherwise.
	 * @param atom
	 * @return one of cAtomCIPParityNone,cAtomCIPParityRorM,cAtomCIPParitySorP,cAtomCIPParityProblem
	 */
	public int getAtomCIPParity(int atom) {
		return (mAtomFlags[atom] & cAtomFlagsCIPParity) >> cAtomFlagsCIPParityShift;
		}


	public int getAtomColor(int atom) {
		return mAtomFlags[atom] & cAtomFlagsColor;
		}

	/**
	 * @return the entire atom coordinate array
	 */
	public Coordinates getAtomCoordinates(int atom) {
		return mCoordinates[atom];
		}


	/**
	 * @return the entire atom coordinate array
	 */
	public Coordinates[] getAtomCoordinates() {
		return mCoordinates;
	}


	/**
	 * This is MDL's enhanced stereo representation (ESR).
	 * Stereo atoms and bonds with the same ESR type (AND or OR) and the same ESR group number
	 * are in the same group, i.e. within this group they have the defined (relative) stereo configuration.
	 * @param atom
	 * @return group index starting with 0
	 */
	public int getAtomESRGroup(int atom) {
		if (getAtomESRType(atom) != cESRTypeAnd
		 && getAtomESRType(atom) != cESRTypeOr)
			return -1;
		else
			return (mAtomFlags[atom] & cAtomFlagsESRGroup) >> cAtomFlagsESRGroupShift;
		}


	/**
	 * This is MDL's enhanced stereo representation (ESR).
	 * Stereo atoms and bonds with the same ESR type (AND or OR) and the same ESR group number
	 * are in the same group, i.e. within this group they have the defined (relative) stereo configuration.
	 * @param atom
	 * @return one of cESRTypeAbs,cESRTypeAnd,cESRTypeOr
	 */
	public int getAtomESRType(int atom) {
		return (mAtomFlags[atom] & cAtomFlagsESRType) >> cAtomFlagsESRTypeShift;
		}


	/**
	 * In addition to the natural atomic numbers, we support additional pseudo atomic numbers.
	 * Most of these are for compatibility with the MDL atom table, e.g. for amino acids and R-groups.
	 * D and T are accepted for setting, but are on-the-fly translated to H with the proper atom mass.
	 * @param atom
	 * @return
	 */
	public int getAtomicNo(int atom) {
		return mAtomicNo[atom];
		}


	/**
	 * If a custom atom label is set, a molecule depiction displays
	 * the custom label instead of the original one.
	 * @param atom
	 * @return null or previously defined atom custom label
	 */
	public String getAtomCustomLabel(int atom) {
		return (mAtomCustomLabel == null) ? null
			 : (mAtomCustomLabel[atom] == null) ? null
			 : new String(mAtomCustomLabel[atom], StandardCharsets.UTF_8);
		}


	/**
	 * This method is more efficient than getAtomCustomLabel(),
	 * because internally atom custom labels are stored as a byte[].
	 * Use this if you can work with bytes and don't need a String.
	 * @param atom
	 * @return null or previously defined atom custom label as byte[]
	 */
	public byte[] getAtomCustomLabelBytes(int atom) {
		return (mAtomCustomLabel == null) ? null : mAtomCustomLabel[atom];
		}


	/**
	 * @param atom
	 * @return standard atom label of the atom: C,Li,Sc,...
	 */
	public String getAtomLabel(int atom) {
		return cAtomLabel[mAtomicNo[atom]];
		}


	/**
	 * The list of atoms that are allowed at this position during sub-structure search.
	 * (or refused atoms, if atom query feature cAtomQFAny is set).
	 * @param atom
	 * @return null or sorted list of unique atomic numbers, if defined
	 */
	public int[] getAtomList(int atom) {
		return (mAtomList == null) ? null : mAtomList[atom];
		}


	public String getAtomListString(int atom) {
		if (mAtomList == null || mAtomList[atom] == null)
			return ((mAtomQueryFeatures[atom] & cAtomQFAny) != 0) ?
						"" : cAtomLabel[mAtomicNo[atom]];

		String listString = "";
		for (int i=0; i 0)
				listString = listString.concat(",");
			int atomicNo = mAtomList[atom][i];
			listString = listString.concat(Molecule.cAtomLabel[atomicNo]);
			}

		return listString;
		}


	/**
	 * Returns an atom mapping number within the context of a reaction.
	 * Atoms that share the same mapping number on the reactant and product side
	 * are considered to be the same atom.
	 * @param atom
	 * @return
	 */
	public int getAtomMapNo(int atom) {
		return Math.abs(mAtomMapNo[atom]);
		}


	/**
	 * @param atom
	 * @return atom mass, if is specific isotop, otherwise 0 for natural abundance
	 */
	public int getAtomMass(int atom) {
		return mAtomMass[atom];
		}


	/**
	 * The atom parity is a calculated property available above/equal helper level cHelperParities.
	 * It describes the stereo configuration of a chiral atom and is calculated either from
	 * 2D-atom-coordinates and up/down-bonds or from 3D-atom-coordinates, whatever is available.
	 * It depends on the atom indexes of the neighbor atoms and their orientation in space.
* The parity is defined as follows: Look at the chiral atom such that its neighbor atom with the * highest atom index (or the hydrogen atom if it is implicit) is oriented to the back. * If the remaining three neighbors are in clockwise order (considering ascending atom indexes) * than the parity is 1. If they are in anti-clockwise order, then the parity is 2.
* For linear chirality (allenes): Look along the straight line of double bonds such that the * rear neighbor with the lower atom index points to the top. If the front neighbor with the * lower atom index points to the right than the parity is 1.
* @param atom * @return one of cAtomParity1,cAtomParity2,cAtomParityNone,cAtomParityUnknown */ public int getAtomParity(int atom) { return mAtomFlags[atom] & cAtomFlagsParity; } /** * Returns all set query features for this atom. In order to get all features related to a certain subject * use something like this: getAtomQueryFeatures() & cAtomQFHydrogen * @param atom * @return */ public long getAtomQueryFeatures(int atom) { return mAtomQueryFeatures[atom]; } /** * Gets an atom's radical state as singulet,dublet,triplet or none * @param atom * @return one of cAtomRadicalStateNone,cAtomRadicalStateS,cAtomRadicalStateD,cAtomRadicalStateT */ public int getAtomRadical(int atom) { return mAtomFlags[atom] & cAtomRadicalState; } public Coordinates getCoordinates(int atom) { return mCoordinates[atom]; } public Coordinates getCenterOfGravity() { if (mAllAtoms == 0) return null; Coordinates c = new Coordinates(); for (int atom = 0; atommAllAtoms) return null; Coordinates c = new Coordinates(); for (int atom:atomIndex) c.add(mCoordinates[atom]); return c.scale(1.0 / atomIndex.length); } public double getAtomX(int atom) { return mCoordinates[atom].x; } public double getAtomY(int atom) { return mCoordinates[atom].y; } public double getAtomZ(int atom) { return mCoordinates[atom].z; } // @Deprecated // public Rectangle2D.Double getBounds(Rectangle2D.Double r) { // if (mAllAtoms == 0) // return null; // // GenericRectangle b = getBounds((GenericRectangle)null); // if (r != null) { // r.x = b.x; // r.y = b.y; // r.width = b.width; // r.height = b.height; // return r; // } // // return new Rectangle2D.Double(b.x, b.y, b.width, b.height): // } public GenericRectangle getBounds(GenericRectangle r) { if (mAllAtoms == 0) return null; double x1 = mCoordinates[0].x; double y1 = mCoordinates[0].y; double x2 = mCoordinates[0].x; double y2 = mCoordinates[0].y; for (int atom=1; atom mCoordinates[atom].x) x1 = mCoordinates[atom].x; else if (x2 < mCoordinates[atom].x) x2 = mCoordinates[atom].x; if (y1 > mCoordinates[atom].y) y1 = mCoordinates[atom].y; else if (y2 < mCoordinates[atom].y) y2 = mCoordinates[atom].y; } if (r == null) { r = new GenericRectangle(x1, y1, x2 - x1, y2 - y1); } else { r.x = x1; r.y = y1; r.width = x2-x1; r.height = y2-y1; } return r; } public static double getDefaultAverageBondLength() { return sDefaultAVBL; } /** * When the molecule adds a new bond to a new atom or a new ring, * then atoms are positioned such that the lengths of the new bonds * are equal to the average length of existing bonds. If there are no * existing bonds, then this default is used. * If the default is not set by this function, then it is 24. * @param defaultAVBL */ public static void setDefaultAverageBondLength(double defaultAVBL) { sDefaultAVBL = defaultAVBL; } /** * Calculates and returns the mean bond length. If the molecule has * no bonds, then the smallest distance between unconnected atoms is * returned. If it has less than 2 atoms, cDefaultAverageBondLength is returned. * @return */ public double getAverageBondLength() { return getAverageBondLength(mAllAtoms, mAllBonds, sDefaultAVBL); } /** * Calculates and returns the mean bond length. If the molecule has * no bonds, then the smallest distance between unconnected atoms is * returned. If it has less than 2 atoms, the given defaultBondLength is returned. * @return */ public double getAverageBondLength(double defaultBondLength) { return getAverageBondLength(mAllAtoms, mAllBonds, defaultBondLength); } /** * Calculates and returns the mean bond length of all bonds 0...bonds. * If there are no bonds, then the smallest distance between unconnected atoms is * returned. If we have less than 2 atoms, cDefaultAverageBondLength is returned. * @param atoms atom indexes >= this are not considered * @param bonds bond indexes >= this are not considered * @return */ public double getAverageBondLength(int atoms, int bonds) { return getAverageBondLength(atoms, bonds, sDefaultAVBL); } /** * Calculates and returns the mean bond length of all bonds 0...bonds. * If there are no bonds, then the smallest distance between unconnected atoms is * determined and a reasonable potential bond length derived from that is returned. * If we have less than 2 atoms, defaultBondLength is returned. * @param atoms atom indexes >= this are not considered * @param bonds bond indexes >= this are not considered * @param defaultBondLength * @return */ public double getAverageBondLength(int atoms, int bonds, double defaultBondLength) { return getAverageBondLength(atoms, bonds, defaultBondLength, mCoordinates); } /** * Calculates and returns the mean bond length of all bonds 0...bonds. * If there are no bonds, then the smallest distance between unconnected atoms is * determined and a reasonable potential bond length derived from that is returned. * If we have less than 2 atoms, defaultBondLength is returned. * @param atoms atom indexes >= these are not considered * @param bonds bond indexes >= these are not considered * @param defaultBondLength * @param coords to be used, either the molecule's coordinates, or an alternative, e.g. from a Conformer * @return */ public double getAverageBondLength(int atoms, int bonds, double defaultBondLength, Coordinates[] coords) { boolean considerMetalBonds = false; int consideredBonds = 0; // count all non-metal bonds for (int bond=0; bond 0 && distance < lowDistance) lowDistance = distance; } } return (lowDistance != Double.MAX_VALUE) ? 0.6 * lowDistance : defaultBondLength; } double avblSum = 0.0; for (int bond=0; bond> cBondFlagsCIPParityShift; } /** * This is MDL's enhanced stereo representation (ESR). * Stereo atoms and bonds with the same ESR type (AND or OR) and the same ESR group number * are in the same group, i.e. within this group they have the defined (relative) stereo configuration. * @param bond * @return group index starting with 0 */ public int getBondESRGroup(int bond) { if (getBondESRType(bond) != cESRTypeAnd && getBondESRType(bond) != cESRTypeOr) return -1; else return (mBondFlags[bond] & cBondFlagsESRGroup) >> cBondFlagsESRGroupShift; } /** * This is MDL's enhanced stereo representation (ESR). * Stereo atoms and bonds with the same ESR type (AND or OR) and the same ESR group number * are in the same group, i.e. within this group they have the defined (relative) stereo configuration. * @param bond * @return one of cESRTypeAbs,cESRTypeAnd,cESRTypeOr */ public int getBondESRType(int bond) { return (mBondFlags[bond] & cBondFlagsESRType) >> cBondFlagsESRTypeShift; } /** * @param bond * @return bond length calculated from atom 2D-coordinates. */ public double getBondLength(int bond) { int atom1 = mBondAtom[0][bond]; int atom2 = mBondAtom[1][bond]; double xdif = mCoordinates[atom2].x - mCoordinates[atom1].x; double ydif = mCoordinates[atom2].y - mCoordinates[atom1].y; return Math.sqrt(xdif * xdif + ydif * ydif); } /** * Returns the formal bond order. Delocalized rings have alternating single and double * bonds, which are returned as such. Bonds that are explicitly marked as being delocalized * are returned as 1. Dative bonds are returned as 0. * In fragments with at least one bond type set as bond query feature, the smallest * non-zero order of allowed bonds is returned. * @param bond * @return formal bond order 0 (dative bonds), 1, 2, 3, 4, or 5 */ public int getBondOrder(int bond) { if (mIsFragment && (mBondQueryFeatures[bond] & cBondQFBondTypes) != 0) { if ((mBondQueryFeatures[bond] & (cBondQFSingle | cBondQFDelocalized)) != 0) return 1; if ((mBondQueryFeatures[bond] & cBondQFDouble) != 0) return 2; if ((mBondQueryFeatures[bond] & cBondQFTriple) != 0) return 3; if ((mBondQueryFeatures[bond] & cBondQFQuadruple) != 0) return 4; if ((mBondQueryFeatures[bond] & cBondQFQuintuple) != 0) return 5; if ((mBondQueryFeatures[bond] & cBondQFMetalLigand) != 0) return 0; } switch (mBondType[bond] & cBondTypeMaskSimple) { case cBondTypeSingle: case cBondTypeDelocalized: return 1; case cBondTypeDouble: return 2; case cBondTypeTriple: return 3; case cBondTypeQuadruple: return 4; case cBondTypeQuintuple: return 5; default: return 0; // dative bonds, ligand field bonds } } /** * Returns the pre-calculated bond parity, e.g. cBondParityEor1. * To distinguish double bond parities (E/Z) from parities of axial * chirality, e.g. BINAP type (1/2) simply check with getBondOrder(bond): * If the order is 2, then the parity describes E/Z, otherwise an axial parity. * @param bnd * @return one of cBondParity??? */ public int getBondParity(int bnd) { return mBondFlags[bnd] & cBondFlagsParity; } public int getBondQueryFeatures(int bnd) { return mBondQueryFeatures[bnd]; } public boolean isBondBridge(int bond) { return (mBondQueryFeatures[bond] & cBondQFBridge) != 0; } public int getBondBridgeMinSize(int bond) { return (mBondQueryFeatures[bond] & cBondQFBridgeMin) >> cBondQFBridgeMinShift; } public int getBondBridgeMaxSize(int bond) { return ((mBondQueryFeatures[bond] & cBondQFBridgeMin) >> cBondQFBridgeMinShift) + ((mBondQueryFeatures[bond] & cBondQFBridgeSpan) >> cBondQFBridgeSpanShift); } /** * Returns bond type combining bond order and stereo orientation. * @param bond * @return one of cBondTypeSingle,cBondTypeDouble,cBondTypeUp,cBondTypeCross,... */ public int getBondType(int bond) { return mBondType[bond]; } /** * This is the bond type without stereo information. * @param bond * @return cBondTypeSingle,cBondTypeDouble,cBondTypeTriple,(cBondTypeDelocalized if used) */ public int getBondTypeSimple(int bond) { return mBondType[bond] & cBondTypeMaskSimple; } /** * Gets the overall chirality of the molecule, which is a calculated information considering: * Recognition of stereo centers and stereo bonds, defined ESR features, meso detection. * The chirality combines the knowledge about how many stereo isomers are represented, * whether all of these are meso, whether we have one defined stereo isomer, a mixture * of racemates, epimers, or other diastereomers. * The information is used during depiction. */ public int getChirality() { return mChirality; } /** * The currently defined maximum of atoms, which increases automatically when using high level * construction methods and new atoms exceed the current maximum. * @return */ public int getMaxAtoms() { return mMaxAtoms; } /** * Used instead of the 1.6 Features. Cartridge needs 1.5 * @param original * @param newLength * @return */ private static Coordinates[] copyOf(Coordinates[] original, int newLength) { Coordinates[] copy = new Coordinates[newLength]; for (int i=0; i * cMoleculeColorNeutral: all atoms and bonds and CIP letters are drawn in neutral color
* @return cMoleculeColorNeutral or cMoleculeColorDefault. In future may also return ARGB values. */ public int getMoleculeColor() { return mMoleculeColor; } /** * Currently, this method only allows to switch the default atomic number dependent atom coloring off * by passing cMoleculeColorNeutral. In future updates it may also accept ARGB values. * @param color currently supported values: cMoleculeColorDefault, cMoleculeColorNeutral */ public void setMoleculeColor(int color) { mMoleculeColor = color; } /** * Allows to set a molecule name or identifier, that is, for instance, written to or read from molfiles. * @return */ public String getName() { return mName; } /** * The stereo problem flag is set by the stereo recognition (available equal/above helper level cHelperParities) * if an atom has over- or under-specified stereo bonds attached, i.e. a stereo center with less or more than one * up/down-bond, an non-stereo-center atom carrying (a) stereo bond(s), or a stereo center with neighbors coordinates * such that the stereo configuration cannot be deduced. This flag is used by the depiction and causes affected atoms * to be drawn in margenta. * @param atom * @return */ public boolean getStereoProblem(int atom) { return ((mAtomFlags[atom] & cAtomFlagStereoProblem) != 0); } /** * @param atom * @return whether the atom's stereo configuration was explicitly declared unknown */ public boolean isAtomConfigurationUnknown(int atom) { return ((mAtomFlags[atom] & cAtomFlagConfigurationUnknown) != 0); } /** * Pseudo paries are parities that indicate a relative configuration. * It always needs at least 2 pseudo parities (atom or bond) within * a part of a molecule to be meaningful. * This information is calculated by ensureHelperArrays(Molecule.cHelperCIP). * Molecules extracted from IDCode don't know about pseudo parities. * @param atom * @return wether this atom's parity is a relative configuration */ public boolean isAtomParityPseudo(int atom) { return ((mAtomFlags[atom] & cAtomParityIsPseudo) != 0); } /** * Atoms with pseudo parities are not considered stereo centers. * While parities are canonized and always refer to the full set * of molecules (in case ESR groups are defined), this method * returns true if this atom is a stereo center in any(!) of the * individual molecules described by the ESR settings. * @param atom * @return true if atom is stereo center in at least one molecule after ESR resolution */ public boolean isAtomStereoCenter(int atom) { return ((mAtomFlags[atom] & cAtomFlagIsStereoCenter) != 0); } public boolean isBondParityPseudo(int bond) { return ((mBondFlags[bond] & cBondParityIsPseudo) != 0); } /** * This hint/flag is set by CoordinateInventor for double bonds without given EZ-parity, * because the new coordinates may imply a not intended EZ-parity. If parities are calculated * later by the Canonizer is can correctly assign cBondParityUnknown if the bond is a stereo bond. * The setBondParity() method clears this flag. * This method usually should not be called for other purposes. * @return whether the bond parity was unknown when 2D- atom coordinates were created */ public boolean isBondParityUnknownOrNone(int bond) { return ((mBondFlags[bond] & cBondParityUnknownOrNone) != 0); } /** * Molecule objects may represent complete molecules or sub-structure fragments, * depending on, whether they are flagges as being a fragment or not. Both representations * have much in common, but in certain aspects behave differently. Thus, complete molecules * are considered to carry implicit hydrogens to fill unoccupied atom valences. * Sub-structure fragments on the other hand may carry atom or bond query features. * Depiction, sub-structure search, and other algorithms treat fragments and complete molecules differerently. * @return */ public boolean isFragment() { return mIsFragment; } public boolean isDelocalizedBond(int bond) { return mBondType[bond] == cBondTypeDelocalized; } /** * @return true if at least one z-coordinate is different from 0.0 */ public boolean is3D() { for (int atom=0; atom= -1 && valence <= 14) { mAtomFlags[atom] &= ~cAtomFlagsValence; mAtomFlags[atom] |= ((1+valence) << cAtomFlagsValenceShift); if (mAtomicNo[atom] == 6) { if (valence == -1 || valence == 0 || valence == 2 || valence == 4) { mAtomFlags[atom] &= ~cAtomRadicalState; if (valence == 2) mAtomFlags[atom] |= cAtomRadicalStateS; } } } } public void setAtomCharge(int atom, int charge) { mAtomCharge[atom] = charge; mValidHelperArrays = cHelperNone; } public void setAtomColor(int atom,int color) { mAtomFlags[atom] &= ~cAtomFlagsColor; mAtomFlags[atom] |= color; } /** * This is a user applied information, rather than a calculated value. * The stereo center configuration is declared to be unknown. * If the atom is recognized a stereo center, then its parity will be cAtomParityUnknown. * @param atom * @param u */ public void setAtomConfigurationUnknown(int atom, boolean u) { if (u) mAtomFlags[atom] |= cAtomFlagConfigurationUnknown; else mAtomFlags[atom] &= ~cAtomFlagConfigurationUnknown; mValidHelperArrays &= cHelperRings; } public void setAtomSelection(int atom,boolean s) { if (s) mAtomFlags[atom] |= cAtomFlagSelected; else mAtomFlags[atom] &= ~cAtomFlagSelected; } /** * Atom marking may be used for any external purpose */ public void setAtomMarker(int atom,boolean s) { if (s) mAtomFlags[atom] |= cAtomFlagMarked; else mAtomFlags[atom] &= ~cAtomFlagMarked; } /** * Set an atom's atomic number and defines the isotop to be natural abundance. * @param atom * @param no */ public void setAtomicNo(int atom,int no) { if ((no >= 0) && (no <= cMaxAtomicNo)) { if (no == 151 || no == 152) { // 'D' or 'T' mAtomicNo[atom] = 1; mAtomMass[atom] = no - 149; } else { mAtomicNo[atom] = no; mAtomMass[atom] = 0; } mAtomFlags[atom] &= ~cAtomFlagsValence; mValidHelperArrays = cHelperNone; } } /** * Defines a list of allowed/excluded atomic numbers for sub-structure matching. * If this atom's query feature cAtomQFAny (any atom) is set, then the list is considered to be a NOT-list. * Depending on cAtomQFAny the list must contain at least 1 or 2 members. * @param atom * @param list null or int[] of valid unique, but not sorted, atomic numbers */ public void setAtomList(int atom, int[] list) { if (mAtomList == null) mAtomList = new int[mMaxAtoms][]; if (list != null) Arrays.sort(list); mAtomList[atom] = list; mValidHelperArrays = cHelperNone; mIsFragment = true; } /** * Defines an atom list as query feature for substructure search * @param atom * @param list is null or a sorted int[] of valid atomic numbers * @param isExcludeList true if atom is a wild card and list contains atoms to be excluded */ public void setAtomList(int atom, int[] list, boolean isExcludeList) { if (list == null) { if (mAtomList != null) mAtomList[atom] = null; return; } if (list.length == 1 && !isExcludeList) { int atomicNo = list[0]; if (mAtomicNo[atom] != atomicNo) changeAtom(atom, atomicNo, 0, -1, 0); if (mAtomList != null) mAtomList[atom] = null; return; } if (mAtomList == null) mAtomList = new int[mMaxAtoms][]; mAtomList[atom] = list; if (isExcludeList) mAtomQueryFeatures[atom] |= cAtomQFAny; mValidHelperArrays = cHelperNone; mIsFragment = true; } /** * Defines an atom mapping number within the context of a reaction. * Atoms that share the same mapping number on the reactant and product side * are considered to be the same atom. * @param atom * @param mapNo * @param autoMapped */ public void setAtomMapNo(int atom, int mapNo, boolean autoMapped) { mAtomMapNo[atom] = (autoMapped) ? -mapNo : mapNo; } /** * Set atom to specific isotop or to have a natural isotop distribution * @param atom * @param mass rounded atom mass or 0 (default) for natural abundance */ public void setAtomMass(int atom, int mass) { mAtomMass[atom] = mass; mValidHelperArrays &= (mAtomicNo[atom] == 1) ? cHelperNone : cHelperRings; } /** * The atom parity is a calculated property available above/equal helper level cHelperParities. * It describes the stereo configuration of a chiral atom and is calculated either from * 2D-atom-coordinates and up/down-bonds or from 3D-atom-coordinates, whatever is available. * It depends on the atom indices of the neighbor atoms and their orientation in space.
* The parity is defined as follows: Look at the chiral atom such that its neighbor atom with the * highest atom index (or the hydrogen atom if it is implicit) is oriented to the back. * If the remaining three neighbors are in clockwise order (considering ascending atom indexes) * than the parity is 1. If they are in anti-clockwise order, then the parity is 2.
* For linear chirality (allenes): Look along the straight line of double bonds such that the * rear neighbor with the lower atom index points to the top. If the front neighbor with the * lower atom index points to the right than the parity is 1.
* This method is called by the Canonizer and usually should not be called otherwise. * @param atom * @param parity one of cAtomParity1,cAtomParity2,cAtomParityNone,cAtomParityUnknown * @param isPseudo true if the configuration is only meaningful relative to another one */ public void setAtomParity(int atom, int parity, boolean isPseudo) { mAtomFlags[atom] &= ~(cAtomFlagsParity | cAtomParityIsPseudo); if (parity != cAtomParityUnknown) mAtomFlags[atom] &= ~cAtomFlagConfigurationUnknown; mAtomFlags[atom] |= parity; if (isPseudo) mAtomFlags[atom] |= cAtomParityIsPseudo; } /** * An atom is considered a stereo center, if it is a stereo center in at least in one of the * molecule configurations represented by the ESR definitions. Pseudo stereo centers are not(!) * considered to be a stereo center. * This method is called by the Canonizer and usually should not be called otherwise. * @param atom * @param isStereoCenter */ protected void setAtomStereoCenter(int atom, boolean isStereoCenter) { mAtomFlags[atom] &= ~cAtomFlagIsStereoCenter; if (isStereoCenter) mAtomFlags[atom] |= cAtomFlagIsStereoCenter; } /** * Introduce or remove an atom query feature and make sure, the molecule is flagged * to be a sub-structure fragment (see setFragment()). * A query feature is usually a flag, which if set, poses an additional atom/bond matching constraint * for the sub-structure search and, thus, reduces the number of matching atoms and therefore also * the number of molecules found. Often multiple query feature flags are related and grouped, e.g. * to define the number of hydrogens atoms. These are the flags related to hydrogen neighbors:

* public static final int cAtomQFHydrogen = 0x00000780;
* public static final int cAtomQFNot0Hydrogen = 0x00000080;
* public static final int cAtomQFNot1Hydrogen = 0x00000100;
* public static final int cAtomQFNot2Hydrogen = 0x00000200;
* public static final int cAtomQFNot3Hydrogen = 0x00000400;
*

An inverse logic needs to be applied to translate a user request to the bits needed. For example, * to only accept atoms that have 1 or 2 hydrogen neighbors, we need to filter out all others. Thus, we * would call
setAtomQueryFeature(atom, cAtomQFNot0Hydrogen | cAtomQFNot3Hydrogen, true);

*

To match only atoms without hydrogen neighbors, call
setAtomQueryFeature(atom, cAtomQFHydrogen & ~cAtomQFNot0Hydrogen, true);
* This mechanism allows a very efficient atom matching and therefore very fast sub-structure search.

* @param atom * @param feature one of cAtomQF... * @param value if true, the feature is set, otherwise it is removed */ public void setAtomQueryFeature(int atom, long feature, boolean value) { if (value) mAtomQueryFeatures[atom] |= feature; else mAtomQueryFeatures[atom] &= ~feature; mValidHelperArrays = 0; // there is an influence on occipied valence, bond order, etc. mIsFragment = true; } /** * Sets an atom's radical state as singulet,dublet,triplet or none * @param atom * @param radical one of cAtomRadicalStateNone,cAtomRadicalStateS,cAtomRadicalStateD,cAtomRadicalStateT */ public void setAtomRadical(int atom, int radical) { mAtomFlags[atom] &= ~cAtomRadicalState; mAtomFlags[atom] |= radical; mValidHelperArrays &= cHelperRings; } /** * The atom Cahn-Ingold-Prelog parity is a calculated property available above/equal helper level cHelperCIP. * It encodes the stereo configuration of an atom with its neighbors using up/down-bonds * or 3D-atom-coordinates, whatever is available. It depends on the atom indices of the neighbor * atoms and their orientation is space. This method is called by the Canonizer and usually should not * be called otherwise. * @param atom * @param parity one of cAtomCIPParityRorM,cAtomCIPParitySorP,cAtomCIPParityProblem */ public void setAtomCIPParity(int atom, int parity) { mAtomFlags[atom] &= ~cAtomFlagsCIPParity; mAtomFlags[atom] |= (parity << cAtomFlagsCIPParityShift); } public void setAtomX(int atom, double x) { mCoordinates[atom].x = x; mValidHelperArrays &= cHelperRings; } public void setAtomY(int atom, double y) { mCoordinates[atom].y = y; mValidHelperArrays &= cHelperRings; } public void setAtomZ(int atom, double z) { mCoordinates[atom].z = z; mValidHelperArrays &= cHelperRings; } public void setBondAtom(int no, int bond, int atom) { mBondAtom[no][bond] = atom; mValidHelperArrays = cHelperNone; } /** * The bond Cahn-Ingold-Prelog parity is a calculated property available above/equal helper level cHelperCIP. * It encodes the stereo configuration of a bond with its neighbors using 2D-coordinates and up/down-bonds * or 3D-atom-coordinates, whatever is available. It depends on the atom indices of the neighbor * atoms and their orientation is space. This method is called by the Canonizer and usually should not * be called otherwise. Considered are E/Z-double bonds and M/P-BINAP type single bonds. * @param bond * @param parity one of cBondCIPParityEorP,cBondCIPParityZorM,cBondCIPParityProblem */ public void setBondCIPParity(int bond, int parity) { mBondFlags[bond] &= ~cBondFlagsCIPParity; mBondFlags[bond] |= (parity << cBondFlagsCIPParityShift); } /** * Used for depiction only. * @param bond * @param s */ public void setBondBackgroundHiliting(int bond, boolean s) { if (s) mBondFlags[bond] |= cBondFlagBGHilited; else mBondFlags[bond] &= ~cBondFlagBGHilited; } /** * Used for depiction only. * @param bond * @param s */ public void setBondForegroundHiliting(int bond, boolean s) { if (s) mBondFlags[bond] |= cBondFlagFGHilited; else mBondFlags[bond] &= ~cBondFlagFGHilited; } /** * The bond parity is a calculated property available above/equal helper level cHelperParities. * It encodes the stereo configuration of a double bond or BINAP type single bond from up/down-bonds * and 2D-coordinates or 3D-atom-coordinates, whatever is available. It depends on the atom indices * of the neighbor atoms and their orientation is space. This method is called by the Canonizer and * usually should not be called otherwise. * @param bond * @param parity one of cBondParityEor1,cBondParityZor2,cBondParityNone,cBondParityUnknown * @param isPseudo true if the configuration is only meaningful relative to another one */ public void setBondParity(int bond, int parity, boolean isPseudo) { mBondFlags[bond] &= ~(cBondFlagsParity | cBondParityIsPseudo | cBondParityUnknownOrNone); mBondFlags[bond] |= parity; if (isPseudo) mBondFlags[bond] |= cBondParityIsPseudo; } /** * This hint/flag is set by CoordinateInventor for double bonds without given EZ-parity, * because the new coordinates may imply a not intended EZ-parity. If parities are calculated * later by the Canonizer is can correctly assign cBondParityUnknown if the bond is a stereo bond. * The setBondParity() method clears this flag. * This method usually should not be called for other purposes. * @param bond */ public void setBondParityUnknownOrNone(int bond) { mBondFlags[bond] |= cBondParityUnknownOrNone; } public void setBondQueryFeature(int bond, int feature, boolean value) { if (value) mBondQueryFeatures[bond] |= feature; else mBondQueryFeatures[bond] &= ~feature; mValidHelperArrays = cHelperNone; // there is an influence on occipied valence, bond order, etc. mIsFragment = true; } /** * Sets the bond type based on bond order without stereo orientation. * @param bond * @param order 1,2, or 3 */ public void setBondOrder(int bond,int order) { mBondType[bond] = (order == 1) ? cBondTypeSingle : (order == 2) ? cBondTypeDouble : (order == 3) ? cBondTypeTriple : cBondTypeMetalLigand; mValidHelperArrays = cHelperNone; } /** * Defines a bond type combining bod order and stereo orientation. * @param bond * @param type one of cBondTypeSingle,cBondTypeDouble,cBondTypeUp,cBondTypeCross,... */ public void setBondType(int bond,int type) { mBondType[bond] = type; mValidHelperArrays = cHelperNone; } /** * Sets the overall chirality of the molecule taking into account: * Recognition of stereo centers and stereo bonds, defined ESR features, meso detection. * The chirality combines the knowledge about how many stereo isomers are represented, * whether all of these are meso, whether we have one defined stereo isomer, a mixture * of racemates, epimers, or other diastereomers. * The information is used during depiction. * This method is called by the Canonizer and usually should not be called otherwise. * @param c */ public void setChirality(int c) { mChirality = c; } /** * Fragment's query features are checked for consistency and normalized * during helper array creation. As part of this, simple hydrogen atoms * are converted into hydrogen-count query features. If hydrogen protection * is enabled, explicit hydrogens are not touched. * @param protectHydrogen */ public void setHydrogenProtection(boolean protectHydrogen) { mProtectHydrogen = protectHydrogen; } /** * Use this method with extreme care. If you make a change to the molecule, * the validity of the helper arrays is typically set to cHelperNone. * If you make a small change to a molecule that doesn't change its topology, * you may override the automatic automatically cleared helper validity with * this method and avoid a new calculation of the neighbour arrays and ring * detection. * @param helperValidity cHelperNeighbours or cHelperRings */ public void setHelperValidity(int helperValidity) { mValidHelperArrays = helperValidity; } /** * This is for compatibility with old MDL stereo representation * that contained a 'chiral' flag to indicate that the molecule * is not a racemate. If a molecule is constructed from a source * format (e.g. a molfile version 2) that contains a 'chiral' flag * then setToRacemate() needs to be called if the chiral flag is * not(!) set. This causes after stereo center recognition to * turn all absolute stereo centers into racemic ones. */ public void setToRacemate() { mIsRacemate = true; } /** * If a custom atom label is set, a molecule depiction displays * the custom label instead of the original one. Custom labels * are not interpreted otherwise. However, they may optionally * be encoded into idcodes; see Canonizer.encodeAtomCustomLabels(). * If a custom label start with ']' then the label without the ']' * symbol is shown at the top left of the original atom label rather than * replacing the original atom label. * the * @param atom * @param label null to remove custom label */ public void setAtomCustomLabel(int atom, byte[] label) { if (label != null && label.length == 0) label = null; if (label == null) { if (mAtomCustomLabel != null) mAtomCustomLabel[atom] = null; } else { if (mAtomCustomLabel == null) mAtomCustomLabel = new byte[mMaxAtoms][]; mAtomCustomLabel[atom] = label; } } /** * If a custom atom label is set, a molecule depiction displays * the custom label instead of the original one. Custom labels * are not interpreted otherwise. However, they may optionally * be encoded into idcodes; see Canonizer.encodeAtomCustomLabels(). * If a custom label start with ']' then the label without the ']' * symbol is shown at the top left of the original atom label rather than * replacing the original atom label. * If label is null or equals the normal atom label, then the custom label * is removed. This method is less efficient than the byte[] version: * setAtomCustomLabel(int, byte[]) * @param atom * @param label null to remove custom label */ public void setAtomCustomLabel(int atom, String label) { if (label != null) { if (label.length() == 0) label = null; else { int atomicNo = getAtomicNoFromLabel(label); if ((atomicNo != 0 && label.equals(cAtomLabel[atomicNo])) || label.equals("?")) { setAtomicNo(atom, atomicNo); label = null; } } } if (label == null) { if (mAtomCustomLabel != null) mAtomCustomLabel[atom] = null; } else { if (mAtomCustomLabel == null) mAtomCustomLabel = new byte[mMaxAtoms][]; mAtomCustomLabel[atom] = label.getBytes(StandardCharsets.UTF_8); } } /** * This is MDL's enhanced stereo representation (ESR). * Stereo atoms and bonds with the same ESR type (AND or OR) and the same ESR group number * are in the same group, i.e. within this group they have the defined (relative) stereo configuration. * @param atom * @param type one of cESRTypeAbs,cESRTypeAnd,cESRTypeOr * @param group index starting with 0 (not considered if type is cESRTypeAbs) */ public void setAtomESR(int atom, int type, int group) { if (type == cESRTypeAbs) { mAtomFlags[atom] &= ~cAtomFlagsESR; mAtomFlags[atom] |= (type << cAtomFlagsESRTypeShift); } else { if (group >= cESRMaxGroups) return; if (group == -1) { // find unused group int maxGroup = -1; for (int i=0; i= cESRMaxGroups) return; } mAtomFlags[atom] &= ~cAtomFlagsESR; mAtomFlags[atom] |= ((type << cAtomFlagsESRTypeShift) | (group << cAtomFlagsESRGroupShift)); } mValidHelperArrays &= cHelperRings; } /** * MDL's enhanced stereo representation for BINAP type of stereo bonds. * Stereo atoms and bonds with the same ESR type (AND or OR) and the same ESR group number * are in the same group, i.e. within this group they have the defined (relative) stereo configuration. * @param bond * @param type one of cESRTypeAbs,cESRTypeAnd,cESRTypeOr * @param group index starting with 0 */ public void setBondESR(int bond, int type, int group) { if (type == cESRTypeAbs) { mBondFlags[bond] &= ~cBondFlagsESR; mBondFlags[bond] |= (type << cBondFlagsESRTypeShift); } else { if (group >= cESRMaxGroups) return; if (group == -1) { // find unused group int maxGroup = -1; for (int i=0; i= cESRMaxGroups) return; } mBondFlags[bond] &= ~cBondFlagsESR; mBondFlags[bond] |= ((type << cBondFlagsESRTypeShift) | (group << cBondFlagsESRGroupShift)); } mValidHelperArrays &= cHelperRings; } /** * Molecule objects may represent complete molecules or sub-structure fragments, * depending on, whether they are flagges as being a fragment or not. Both representations * have much in common, but in certain aspects behave differently. Thus, complete molecules * are considered to carry implicit hydrogens to fill unoccupied atom valences. * Sub-structure fragments on the other hand may carry atom or bond query features. * Depiction, sub-structure search, and other algorithms treat fragments and complete molecules * differently. * @param isFragment if false, then all query features are removed */ public void setFragment(boolean isFragment) { if (mIsFragment != isFragment) { mIsFragment = isFragment; if (!isFragment) removeQueryFeatures(); mValidHelperArrays = cHelperNone; } } public void setName(String name) { mName = name; } public Object getUserData() { return mUserData; } public void setUserData(Object userData) { mUserData = userData; } /** * Removes any query features from the molecule * @return whether any query features were removed */ public boolean removeQueryFeatures() { boolean isChanged = false; for (int atom=0; atom max) maxBondOrder = max; } return maxBondOrder; } private boolean incrementBondOrder(int bond) { int maxBondOrder = getMaximumBondOrder(bond); boolean hasMetal = isMetalAtom(mBondAtom[0][bond]) || isMetalAtom(mBondAtom[1][bond]); int startBond = hasMetal ? cBondTypeMetalLigand : cBondTypeSingle; if (mBondType[bond] == cBondTypeQuintuple) { mBondType[bond] = startBond; mValidHelperArrays = cHelperNone; return true; } if (mBondType[bond] == cBondTypeQuadruple) { mBondType[bond] = (maxBondOrder > 4) ? cBondTypeQuintuple : startBond; mValidHelperArrays = cHelperNone; return true; } if (mBondType[bond] == cBondTypeTriple) { mBondType[bond] = (maxBondOrder > 3) ? cBondTypeQuadruple : startBond; mValidHelperArrays = cHelperNone; return true; } if (mBondType[bond] == cBondTypeDouble) { mBondType[bond] = cBondTypeCross; mValidHelperArrays &= cHelperRings; if ((mBondFlags[bond] & cBondFlagSmallRing) == 0) return true; } if (mBondType[bond] == cBondTypeCross) { if (maxBondOrder > 2) mBondType[bond] = cBondTypeTriple; else mBondType[bond] = startBond; mValidHelperArrays = cHelperNone; return true; } if ((cBondTypeMaskStereo & mBondType[bond]) != 0) { mBondType[bond] = cBondTypeSingle; mValidHelperArrays &= cHelperRings; return true; } if (!hasMetal && maxBondOrder < 2) return false; if (mBondType[bond] == cBondTypeSingle) { mBondType[bond] = cBondTypeDouble; mValidHelperArrays = cHelperNone; return true; } if (maxBondOrder < 1) return false; if (mBondType[bond] == cBondTypeMetalLigand) { mBondType[bond] = cBondTypeSingle; mValidHelperArrays = cHelperNone; return true; } return false; } protected boolean validateBondType(int bond, int type) { int simpleType = type & Molecule.cBondTypeMaskSimple; int maxBondOrder = getMaximumBondOrder(bond); switch (simpleType) { case cBondTypeSingle: case cBondTypeDelocalized: return maxBondOrder >= 1; case cBondTypeDouble: return maxBondOrder >= 2; case cBondTypeTriple: return maxBondOrder >= 3; case cBondTypeQuadruple: return maxBondOrder >= 4; case cBondTypeQuintuple: return maxBondOrder >= 5; case cBondTypeMetalLigand: return true; default: return false; } } /** * The sum of bond orders of explicitly connected neighbour atoms. * @param atom * @return explicitly used valence */ protected int getOccupiedValence(int atom) { // ExtendedMolecule overwrites this function by updateing and using neighbour arrays return simpleGetValence(atom); } private int simpleGetValence(int atom) { int val=0; // this version doesn't change helper arrays for (int bnd=0; bnd 3. * @param atom * @return */ public int getMaxValenceUncharged(int atom) { int valence = getAtomAbnormalValence(atom); if (valence == -1) valence = getDefaultMaxValenceUncharged(atom); return valence; } /** * This is the default maximum valence of the atom * neglecting atom charge or radical influences, e.g. N or N(+) -> 3. * If the atomic no has multiple valid max valences, it is the highest one. * @param atom * @return */ public int getDefaultMaxValenceUncharged(int atom) { byte[] valenceList = (mAtomicNo[atom] < cAtomValence.length) ? cAtomValence[mAtomicNo[atom]] : null; return (valenceList == null) ? cDefaultAtomValence : valenceList[valenceList.length-1]; } /** * This is the defined maximum valence (or set abnormal valence) * corrected by atom charge or radical influences, e.g. N(+) -> 4. * @param atom * @return */ public int getMaxValence(int atom) { int valence = getMaxValenceUncharged(atom); return valence + getElectronValenceCorrection(atom, valence); } /** * This is the maximum valence correction caused by atom charge * or radical status, e.g. N+ -> 1; N- -> -1; Al+ -> -1; C+,C- -> -1. * In some cases, where the atomicNo can have multiple valences, * the influence of a charge depends on the atom's actual valence, e.g. * valence corrections for R3P(+) and R5P(+) are 1 and -1, respectively. * Criteria are:
* -in the given valence state is there a lone pair that can be protonated
* -can we introduce a negative substituent as in BH3 or PF5 vs. SF6
* @param atom * @param occupiedValence * @return */ public int getElectronValenceCorrection(int atom, int occupiedValence) { return getElectronValenceCorrection(atom, occupiedValence, mAtomicNo[atom]); } protected int getElectronValenceCorrection(int atom, int occupiedValence, int atomicNo) { if (atomicNo >= 171 && atomicNo <= 190) return 0; int correction = 0; if ((mAtomFlags[atom] & cAtomRadicalState) == cAtomRadicalStateD) correction -= 1; if ((mAtomFlags[atom] & cAtomRadicalState) == cAtomRadicalStateS || (mAtomFlags[atom] & cAtomRadicalState) == cAtomRadicalStateT) correction -= 2; int charge = mAtomCharge[atom]; if (charge == 0 && mIsFragment) { if ((mAtomQueryFeatures[atom] & cAtomQFCharge) == cAtomQFNotCharge0+cAtomQFNotChargePos) charge = -1; if ((mAtomQueryFeatures[atom] & cAtomQFCharge) == cAtomQFNotCharge0+cAtomQFNotChargeNeg) charge = 1; } if (atomicNo == 7 // N || atomicNo == 8 // O || atomicNo == 9) // F correction += charge; else if (atomicNo == 6 // C || atomicNo == 14 // Si || atomicNo == 32) // Ge correction -= Math.abs(charge); else if (atomicNo == 15 // P || atomicNo == 33) { // As if (occupiedValence - correction - charge <= 3) correction += charge; else correction -= charge; } else if (atomicNo == 16 // S || atomicNo == 34 // Se || atomicNo == 52) { // Te if (occupiedValence - correction - charge <= 4) correction += charge; else correction -= Math.abs(charge); } else if (atomicNo == 17 // Cl || atomicNo == 35 // Br || atomicNo == 53) { // I if (occupiedValence - correction - charge <= 5) correction += charge; else correction -= Math.abs(charge); } else { // B, Al, other metals correction -= charge; } return correction; } public static boolean isAtomicNoElectronegative(int atomicNo) { switch (atomicNo) { case 7: // N case 8: // O case 9: // F case 15: // P case 16: // S case 17: // Cl case 33: // As case 34: // Se case 35: // Br case 52: // Te case 53: // I return true; } return false; } /** * @param atom * @return whether atom is an electronegative one */ public boolean isElectronegative(int atom) { if (mIsFragment) { if ((mAtomQueryFeatures[atom] & cAtomQFAny) != 0) return false; if (mAtomList != null && mAtomList[atom] != null) for (int atomicNo:mAtomList[atom]) if (!isAtomicNoElectronegative(atomicNo)) return false; } return isAtomicNoElectronegative(mAtomicNo[atom]); } public static boolean isAtomicNoElectropositive(int atomicNo) { if (atomicNo == 1 || atomicNo == 6) return false; if (isAtomicNoElectronegative(atomicNo)) return false; if (atomicNo == 2 // He || atomicNo == 10 // Ne || atomicNo == 18 // Ar || atomicNo == 36 // Kr || atomicNo == 54) // Xe return false; if (atomicNo > 103) // amino acids etc. return false; return true; } /** * @param atom * @return whether atom is an electropositive one */ public boolean isElectropositive(int atom) { if (mIsFragment) { if ((mAtomQueryFeatures[atom] & cAtomQFAny) != 0) return false; if (mAtomList != null && mAtomList[atom] != null) for (int atomicNo:mAtomList[atom]) if (!isAtomicNoElectropositive(atomicNo)) return false; } return isAtomicNoElectropositive(mAtomicNo[atom]); } /** * @param atom * @return whether atom is a metal atom */ public boolean isMetalAtom(int atom) { if (mIsFragment) { if ((mAtomQueryFeatures[atom] & cAtomQFAny) != 0) return false; if (mAtomList != null && mAtomList[atom] != null) for (int atomicNo:mAtomList[atom]) if (!isAtomicNoMetal(atomicNo)) return false; } return isAtomicNoMetal(mAtomicNo[atom]); } /** * @param atom * @return whether atom is a transition metal atom */ public boolean isTransitionMetalAtom(int atom) { if (mIsFragment) { if ((mAtomQueryFeatures[atom] & cAtomQFAny) != 0) return false; if (mAtomList != null && mAtomList[atom] != null) for (int atomicNo:mAtomList[atom]) if (!isAtomicNoTransitionMetal(atomicNo)) return false; } return isAtomicNoTransitionMetal(mAtomicNo[atom]); } public static boolean isAtomicNoMetal(int atomicNo) { return (atomicNo >= 3 && atomicNo <= 4) || (atomicNo >= 11 && atomicNo <= 13) || (atomicNo >= 19 && atomicNo <= 31) || (atomicNo >= 37 && atomicNo <= 51) || (atomicNo >= 55 && atomicNo <= 84) || (atomicNo >= 87 && atomicNo <= 112); } public static boolean isAtomicNoTransitionMetal(int atomicNo) { return (atomicNo >= 21 && atomicNo <= 30) || (atomicNo >= 39 && atomicNo <= 48) || atomicNo == 57 || (atomicNo >= 72 && atomicNo <= 80) || atomicNo == 89 || (atomicNo >= 104 && atomicNo <= 112); } /** * @param atom * @return true if this atom is not a metal and not a nobel gas */ public boolean isOrganicAtom(int atom) { if (mIsFragment) { if ((mAtomQueryFeatures[atom] & cAtomQFAny) != 0) return false; if (mAtomList != null && mAtomList[atom] != null) for (int atomicNo:mAtomList[atom]) if (!isAtomicNoOrganic(atomicNo)) return false; } return isAtomicNoOrganic(mAtomicNo[atom]); } public static boolean isAtomicNoOrganic(int atomicNo) { return atomicNo == 1 || (atomicNo >= 5 && atomicNo <= 9) // B,C,N,O,F || (atomicNo >= 14 && atomicNo <= 17) // Si,P,S,Cl || (atomicNo >= 32 && atomicNo <= 35) // Ge,As,Se,Br || (atomicNo >= 52 && atomicNo <= 53); // Te,I } public void removeAtomMapping(boolean keepManualMapping) { for (int atom=0; atom




© 2015 - 2024 Weber Informatics LLC | Privacy Policy