content: /*
   * 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.
   */
  
  /*
  * 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.calc.ArrayUtilsCalc;
  import com.actelion.research.calc.statistics.StatisticsOverview;
  import com.actelion.research.calc.statistics.median.MedianStatisticFunctions;
  import com.actelion.research.chem.descriptor.DescriptorEncoder;
  import com.actelion.research.chem.descriptor.DescriptorHandler;
  import com.actelion.research.chem.mcs.ListWithIntVec;
  import com.actelion.research.util.BurtleHasher;
  import com.actelion.research.util.Formatter;
  import com.actelion.research.util.datamodel.IntegerDouble;
  
  import java.util.*;
  
  /**
   *
   *
   * ExtendedMoleculeFunctions
   * @author Modest von Korff
   * @version 1.0
   * 2005 MvK: Start implementation
   */
  public class ExtendedMoleculeFunctions {
  
  
  	public static final int NUMBER_ELEMENTS = 118;
  
  	public static final int COLOR_FOR_CENTER_SELECTION = Molecule.cAtomColorRed;
  
  
  	public static final int [] arrRGroupsAtomicNo = {142,143,144,129,130,131,132,133,134,135,136,137,138,139,140,141};
  
  	public static final String [] arrRGroupsSymbol = {"R1","R2","R3","R4","R5","R6","R7","R8","R9","R10","R11","R12","R13","R14","R15","R16"};
  
  
  	public final static Coordinates getCenterGravity(ExtendedMolecule mol) {
  		int n = mol.getAllAtoms();
  		int [] indices = new int [n];
  		for (int i = 0; i < indices.length; i++) {
  			indices[i]=i;
  		}
  		return getCenterGravity(mol, indices);
  	}
  
  	public final static Coordinates getCenterGravity(ExtendedMolecule mol, int[] indices) {
  		Coordinates c = new Coordinates();
  		for (int i = 0; i < indices.length; i++) {
  			c.x += mol.getAtomX(indices[i]);
  			c.y += mol.getAtomY(indices[i]);
  			c.z += mol.getAtomZ(indices[i]);
  		}
  		c.x /= indices.length;
  		c.y /= indices.length;
  		c.z /= indices.length;
  		return c;
  	}
  
  
  	public static void makeSkeleton(StereoMolecule mol) {
  		for (int bond=0; bond<mol.getAllBonds(); bond++)
  			mol.setBondType(bond, Molecule.cBondTypeSingle);
  		for (int atom=0; atom<mol.getAllAtoms(); atom++)
  			mol.setAtomicNo(atom, 6);
  	}
  
  	public static void analyzeMolecule(StereoMolecule mol){
  		int nAtoms = mol.getAtoms();
  		for (int atom = 0; atom < nAtoms; atom++) {
  			if(AtomFunctionAnalyzer.isAcidicOxygen(mol, atom)){
  				System.out.println("Acidic oxygen " + atom);
  			}
  			if(AtomFunctionAnalyzer.isNitroGroupN(mol, atom)){
  				System.out.println("Nitro group N " + atom);
  			}
  			if(AtomFunctionAnalyzer.isMemberOfNitroGroup(mol, atom)){
  				System.out.println("Member of group " + atom);
  			}
  			if(AtomFunctionAnalyzer.isBasicNitrogen(mol, atom)){
  				System.out.println("Basic nitrogen " + atom);
  			}
  		}
  		System.out.println("Index\tAtomic no\tAtom type");
  		for (int indexAtom = 0; indexAtom < mol.getAllAtoms(); indexAtom++) {
  			long atomType=-1;
  			try {
  				atomType = AtomTypeCalculator.getAtomType(mol, indexAtom, AtomTypeCalculator.cPropertiesForCLogPCharges);
  			} catch (Exception e) {
  				e.printStackTrace();
  			}
  			System.out.println(indexAtom + "\t" + mol.getAtomicNo(indexAtom) + "\t" + atomType + "\t" + AtomTypeCalculator.toString(atomType));
  		}
  		System.out.println();
  	}
  
  	public static String getColorVal2String(Molecule mol, int indexAtom){
  		int color = mol.getAtomColor(indexAtom);
  		return getcAtomColor2String(color);
  	}
  
  	/**
  	 *
  	 * @param cAtomColor atom color value from Molecule.
  	 * @return
  	 */
  	public static String getcAtomColor2String(int cAtomColor){
  		String sColor = "";
  		switch (cAtomColor) {
  			case Molecule.cAtomColorNone:
  				sColor = "black";
  				break;
  			case Molecule.cAtomColorBlue:
  				sColor = "blue";
  				break;
  			case Molecule.cAtomColorRed:
  				sColor = "red";
  				break;
  			case Molecule.cAtomColorGreen:
  				sColor = "green";
  				break;
  			case Molecule.cAtomColorMagenta:
  				sColor = "magenta";
  				break;
  			case Molecule.cAtomColorOrange:
  				sColor = "orange";
  				break;
  			case Molecule.cAtomColorDarkGreen:
  				sColor = "darkGreen";
  				break;
  			case Molecule.cAtomColorDarkRed:
  				sColor = "darkRed";
  				break;
  
  			default:
  				break;
  		}
  		return sColor;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param liIndexAtom
  	 * @param cAtomColor atom color value from Molecule.
  	 * @return
  	 */
  	public static String getColorRecord(Molecule mol, Collection<Integer> liIndexAtom, int cAtomColor){
  		StringBuilder sb = new StringBuilder();
  		int cc=0;
  		for (int indexAtom : liIndexAtom) {
  			String sColor = getcAtomColor2String(cAtomColor);
  			sColor += ":"+indexAtom;
  			sb.append(sColor);
  			if(cc<liIndexAtom.size()-1){
  				sb.append(";");
  			}
  			cc++;
  		}
  		return sb.toString();
  	}
  
  	public static int getNumQueryAtoms(ExtendedMolecule mol, int [] arrAtomicNoQuery) {
  		int hetero = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			for (int j = 0; j < arrAtomicNoQuery.length; j++) {
  				if(arrAtomicNoQuery[j]==atomicNo){
  					hetero++;
  					break;
  				}
  			}
  		}
  		return hetero;
  	}
  
  	public static int getNumBondsNoHydrogen(ExtendedMolecule mol) {
  		int bnds = 0;
  		for (int i = 0; i < mol.getBonds(); i++) {
  			int at1 = mol.getBondAtom(0,i);
  			int atNo1 = mol.getAtomicNo(at1);
  			int at2 = mol.getBondAtom(1,i);
  			int atNo2 = mol.getAtomicNo(at2);
  			if(atNo1!=1 && atNo2!=1) {
  				bnds++;
  			}
  		}
  		return bnds;
  	}
  	public static int getNumNonHydrogenAtoms(ExtendedMolecule mol) {
  		return getNumNonHydrogenAtoms(mol, 1000);
  	}
  	public static int getNumNonHydrogenAtoms(ExtendedMolecule mol, int atNosNot2Consider) {
  		int non = 0;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			if((atomicNo==1) || (atomicNo>=atNosNot2Consider))
  				continue;
  			non++;
  		}
  		return non;
  	}
  	public static int getNumCarbonAtoms(ExtendedMolecule mol) {
  		int carbon = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			if(atomicNo==6) {
  				carbon++;
  			}
  		}
  		return carbon;
  	}
  
  	public static int getNumHeteroAtoms(ExtendedMolecule mol) {
  		int hetero = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			if((atomicNo>1) &&
  					(atomicNo != 6) &&
  					(atomicNo <= NUMBER_ELEMENTS)) {
  				hetero++;
  			}
  		}
  		return hetero;
  	}
  
  	public static boolean isHetero(ExtendedMolecule mol, int indexAtom){
  		boolean hetero = false;
  		int atomicNo = mol.getAtomicNo(indexAtom);
  		if((atomicNo>1) &&
  				(atomicNo != 6) &&
  				(atomicNo <= NUMBER_ELEMENTS)) {
  			hetero = true;
  		}
  		return hetero;
  	}
  
  	public static int getNumNitroGroupN(StereoMolecule mol) {
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(AtomFunctionAnalyzer.isNitroGroupN(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumAmide(StereoMolecule mol) {
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(AtomFunctionAnalyzer.isAmide(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumCarboxy(StereoMolecule mol) {
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  
  			if(isCarboxyC(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  
  	public static int getNumAcidicOxygen(StereoMolecule mol) {
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(AtomFunctionAnalyzer.isAcidicOxygen(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumBasicNitrogen(StereoMolecule mol) {
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(AtomFunctionAnalyzer.isBasicNitrogen(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  	public static int getNumAliphaticRingAtoms(ExtendedMolecule mol, int atomicNoQuery) {
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			int atomicNo = mol.getAtomicNo(i);
  
  			if(mol.isRingAtom(i) && atomicNo==atomicNoQuery){
  				n++;
  			}
  		}
  		return n;
  	}
  	/**
  	 *
  	 * @param mol
  	 * @return number of atoms which are not hydrogen.
  	 */
  	public static int getNumHeavyAtoms(ExtendedMolecule mol) {
  		int heavy = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			if(atomicNo>1 && atomicNo <= NUMBER_ELEMENTS) {
  				heavy++;
  			}
  		}
  		return heavy;
  	}
  
  	public static int getNumAromaticAtoms(ExtendedMolecule mol) {
  		int n=0;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(mol.isAromaticAtom(i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumArylAmine(StereoMolecule mol) {
  		int n=0;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(AtomFunctionAnalyzer.isArylAmine(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumHeteroAromaticAtoms(ExtendedMolecule mol) {
  		int n=0;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(mol.isRingAtom(i) && mol.isAromaticAtom(i) && mol.getAtomicNo(i)!= 6){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumIsopropyl(ExtendedMolecule mol) {
  		int n=0;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(mol.isRingAtom(i) && mol.isAromaticAtom(i) && mol.getAtomicNo(i)!= 6){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumSubstructure(StereoMolecule mol, String idcodeFragment) {
  		IDCodeParser parser = new IDCodeParser();
  		StereoMolecule frag = parser.getCompactMolecule(idcodeFragment);
  		frag.ensureHelperArrays(Molecule.cHelperRings);
  		SSSearcher ssSearcher = new SSSearcher();
  		ssSearcher.setMol(frag, mol);
  		int nMatches = ssSearcher.findFragmentInMolecule();
  		return nMatches;
  	}
  
  
  
  	public static int getBondNo(ExtendedMolecule mol, int atm1,int atm2) {
  		for (int bnd=0; bnd<mol.getAllBonds(); bnd++)
  			if (((mol.getBondAtom(0,bnd) == atm1)
  					&& (mol.getBondAtom(1,bnd) == atm2))
  					|| ((mol.getBondAtom(0,bnd) == atm2)
  					&& (mol.getBondAtom(1,bnd) == atm1)))
  				return bnd;
  		return -1;
  	}
  
  	public static int getBondOrder(ExtendedMolecule mol, int atm1,int atm2) {
  		int bndno = getBondNo(mol, atm1,atm2);
  		if(bndno==-1)
  			return -1;
  		int bndord = mol.getBondOrder(bndno);
  		return bndord;
  	}
  
  	public static int getBondType(ExtendedMolecule mol, int atm1,int atm2) {
  		int bndno = getBondNo(mol, atm1,atm2);
  		if(bndno==-1)
  			return -1;
  		int bndtype = mol.getBondType(bndno);
  		return bndtype;
  	}
  
  	public static int getBondParity(ExtendedMolecule mol, int atm1,int atm2) {
  		int bndno = getBondNo(mol, atm1,atm2);
  		if(bndno==-1)
  			return -1;
  		int bndtype = mol.getBondParity(bndno);
  		return bndtype;
  	}
  	public static boolean isPseudoParity(ExtendedMolecule mol, int atm1,int atm2) {
  		int bndno = getBondNo(mol, atm1,atm2);
  		if(bndno==-1)
  			return false;
  		return mol.isBondParityPseudo(bndno);
  	}
  
  	public static boolean deleteBond(ExtendedMolecule mol, int atm1,int atm2) {
  		int bndno = getBondNo(mol, atm1,atm2);
  		if(bndno==-1)
  			return false;
  		mol.deleteBond(bndno);
  		return true;
  	}
  
  	public static String getBiggestFragmentIDCode(String idCode) {
  		StereoMolecule mol = new StereoMolecule();
  		new IDCodeParser(false).parse(mol, idCode);
  		StereoMolecule [] frags = mol.getFragments();
  		String idBiggest = idCode;
  		if(frags.length > 1) {
  			StereoMolecule biggest = mol;
  			int maxAtoms = 0;
  			for (int ii = 0; ii < frags.length; ii++) {
  				if (frags[ii].getAllAtoms() > maxAtoms) {
  					maxAtoms = frags[ii].getAllAtoms();
  					biggest = frags[ii];
  				}
  			}
  			Canonizer canFrag = new Canonizer(biggest);
  			idBiggest = canFrag.getIDCode();
  		}
  		return idBiggest;
  	}
  
  	public static StereoMolecule getBiggestFragment(StereoMolecule mol) {
  		StereoMolecule [] frags = mol.getFragments();
  		StereoMolecule biggest = mol;
  		if(frags.length > 1) {
  			int maxAtoms = 0;
  			for (int ii = 0; ii < frags.length; ii++) {
  				if (frags[ii].getAllAtoms() > maxAtoms) {
  					maxAtoms = frags[ii].getAllAtoms();
  					biggest = frags[ii];
  				}
  			}
  		}
  		return biggest;
  	}
  
  	/**
  	 * Replaces all hetero-atoms, except hydrogen, with carbon.
  	 * @param m
  	 * @return
  	 */
  	public static StereoMolecule getConverted2CarbonSkeleton(StereoMolecule m){
  		StereoMolecule skel = new StereoMolecule(m);
  		skel.ensureHelperArrays(Molecule.cHelperRings);
  		for (int i = 0; i < skel.getAtoms(); i++) {
  
  			if(skel.getAtomicNo(i)>1) {
  				skel.setAtomicNo(i, 6);
  			}
  		}
  		skel.ensureHelperArrays(Molecule.cHelperRings);
  		return skel;
  	}
  
  	public static Comparator<StereoMolecule> getComparatorAtomsBonds(){
  		return new Comparator<StereoMolecule>() {
  			@Override
  			public int compare(StereoMolecule m1, StereoMolecule m2) {
  				if(m1.getAllAtoms() > m2.getAllAtoms()){
  					return 1;
  				}else if(m1.getAllAtoms() < m2.getAllAtoms()){
  					return -1;
  				} else if(m1.getAllBonds() > m2.getAllBonds()){
  					return 1;
  				} else if(m1.getAllBonds() < m2.getAllBonds()){
  					return -1;
  				}
  				return 0;
  			}
  		};
  	}
  
  	public static boolean checkBiggestFragmentForUnwanted(StereoMolecule mol, List<Integer> liAtomicNo) {
  		boolean bOk=true;
  		ExtendedMolecule [] frags = mol.getFragments();
  		int indexBiggestFrag = 0;
  		if(frags.length > 1) {
  			int maxAtoms = 0;
  			for (int ii = 0; ii < frags.length; ii++) {
  				if (frags[ii].getAllAtoms() > maxAtoms) {
  					indexBiggestFrag = ii;
  					maxAtoms = frags[ii].getAllAtoms();
  				}
  			}
  		}
  		ExtendedMolecule frag = frags[indexBiggestFrag];
  		for (int i = 0; i < frag.getAllAtoms(); i++) {
  			if(liAtomicNo.contains(Integer.valueOf(frag.getAtomicNo(i)))) {
  				bOk=false;
  				break;
  			}
  		}
  		return bOk;
  	}
  	/**
  	 *
  	 * @param mol
  	 * @param hsAtomicNo
  	 * @return true if an atomic number from the hash set is found.
  	 */
  	public static boolean containsAtLeastOneAtomicNumbersFromHashSet(ExtendedMolecule mol, HashSet<Integer> hsAtomicNo) {
  		boolean bOk=false;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(hsAtomicNo.contains(mol.getAtomicNo(i))) {
  				bOk=true;
  				break;
  			}
  		}
  		return bOk;
  	}
  
  	public static boolean containsHeteroAtom(ExtendedMolecule mol, int [] arrIndexAt) {
  		boolean hetero=false;
  		for (int indexAt : arrIndexAt) {
  			if(mol.getAtomicNo(indexAt)!=6 && mol.getAtomicNo(indexAt)!=1){
  				hetero=true;
  				break;
  			}
  		}
  		return hetero;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param hsAtomicNo
  	 * @return true if the molecule contains an atomic number that is not in the hash set.
  	 */
  	public static boolean containsSolelyAtomicNumbersFromHashSet(ExtendedMolecule mol, HashSet<Integer> hsAtomicNo) {
  		boolean allAtomicNosInHashset=true;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(!hsAtomicNo.contains(mol.getAtomicNo(i))) {
  				allAtomicNosInHashset=false;
  				break;
  			}
  		}
  		return allAtomicNosInHashset;
  	}
  
  	/**
  	 *
  	 * @param molecule
  	 * @param at index to specify atom in molecule
  	 * @param fragment one atom must have the color: COLOR_FOR_CENTER_SELECTION.
  	 * @return true if the specified atom in molecule equals the colored atom in fragment.
  	 */
  	public final static boolean atomAtomSubStrucMatch(StereoMolecule molecule, int at, StereoMolecule fragment) {
  		boolean bMatch = false;
  		SSSearcher sss = new SSSearcher();
  		sss.setMol(fragment, molecule);
  		int numFrags = sss.findFragmentInMolecule(SSSearcher.cCountModeOverlapping, SSSearcher.cMatchAromDBondToDelocalized);
  		if(numFrags == 0)
  			return false;
  		List<int []> liAtomLists = sss.getMatchList();
  		ArrayUtilsCalc.removeDoubletsIntOrderIndepend(liAtomLists);
  		int atomMarkedInQuery = -1;
  		for (int i = 0; i < fragment.getAllAtoms(); i++) {
  			if(fragment.getAtomColor(i)== COLOR_FOR_CENTER_SELECTION){
  				atomMarkedInQuery = i;
  			}
  		}
  		for (Iterator<int []> iter = liAtomLists.iterator(); iter.hasNext();) {
  			int[] arrInd = (int[]) iter.next();
  			if(arrInd[atomMarkedInQuery] == at){
  				bMatch = true;
  				break;
  			}
  		}
  		return bMatch;
  	}
  
  	public static double [][] getDistanceArray(ExtendedMolecule mol) {
  		double arr[][]= new double[mol.getAllAtoms()][mol.getAllAtoms()];
  		for (int i = 0; i < arr.length; i++) {
  			for (int j = i+1; j < arr.length; j++) {
  				double dx = mol.getAtomX(i) - mol.getAtomX(j);
  				double dy = mol.getAtomY(i) - mol.getAtomY(j);
  				double dz = mol.getAtomZ(i) - mol.getAtomZ(j);
  				double v= Math.sqrt(dx*dx+dy*dy+dz*dz);
  				arr[i][j] = v;
  				arr[j][i] = v;
  			}
  		}
  		return arr;
  	}
  
  	public final static int [] getTopologicalDistances(int [][] topoDistMatrix, int [] at1, int [] at2) {
  		int [] d = new int[at1.length*at2.length];
  		int cc=0;
  		for (int i = 0; i < at1.length; i++) {
  			for (int j = 0; j < at2.length; j++) {
  				d[cc++]=topoDistMatrix[at1[i]][at2[j]];
  			}
  		}
  		return d;
  	}
  	public final static int getMedianTopologicalDistance(int [][] topoDistMatrix, int [] at1, int [] at2) {
  		int [] d = getTopologicalDistances(topoDistMatrix, at1, at2);
  		int medTopoDist = MedianStatisticFunctions.getMedianForInteger(d).median;
  		return medTopoDist;
  	}
  
  	public static List<StereoMolecule> parseIdCodes(List<String> idcodes){
  		List<StereoMolecule> mols = new ArrayList<>();
  		IDCodeParser parser = new IDCodeParser();
  		for (String idcode : idcodes) {
  			StereoMolecule mol = parser.getCompactMolecule(idcode);
  			mol.ensureHelperArrays(Molecule.cHelperRings);
  			mols.add(mol);
  		}
  
  		return mols;
  	}
  
  	public final static int getTopologicalDistance(ExtendedMolecule mol, int at1, int at2) {
  		int dist = 0;
  		if(at1 == at2)
  			return 0;
  		List<Integer> liExamine = new ArrayList<Integer>();
  		List<Integer> liSphere = new ArrayList<Integer>();
  		List<Integer> liVisited = new ArrayList<Integer>();
  		liExamine.add(Integer.valueOf(at1));
  		liSphere.add(Integer.valueOf(1));
  		boolean bFound = false;
  		while(!liExamine.isEmpty()) {
  			dist = ((Integer)liSphere.remove(0)).intValue();
  			int indAtCenter = ((Integer)liExamine.remove(0)).intValue();
  			liVisited.add(Integer.valueOf(indAtCenter));
  			int numJNeighbors = mol.getAllConnAtoms(indAtCenter);
  			for (int i = 0; i < numJNeighbors; i++) {
  				int indAtNeighb = mol.getConnAtom(indAtCenter, i);
  				if(indAtNeighb == at2) {
  					bFound = true;
  					break;
  				}
  				if(!liVisited.contains(Integer.valueOf(indAtNeighb))) {
  					liExamine.add(Integer.valueOf(indAtNeighb));
  					liSphere.add(Integer.valueOf(dist + 1));
  				}
  			}
  			if(bFound)
  				break;
  		}
  		if(!bFound)
  			dist = -1;
  		return dist;
  	}
  
  	public final static int [][] getTopologicalDistanceMatrix(StereoMolecule mol) {
  		return getNumberOfBondsBetweenAtoms(mol, mol.getBonds(), null);
  	}
  
  	public static int getMaximumTopologicalDistance(int [][] topoDistance){
  		int max=0;
  		for (int i = 0; i < topoDistance.length; i++) {
  			for (int j = i+1; j < topoDistance.length; j++) {
  				if(topoDistance[i][j]>max)
  					max=topoDistance[i][j];
  			}
  		}
  		return max;
  	}
  
  	/**
  	 * From Joel Freyss developed for the FFMolecule
  	 * Computes a matrix of distances between all the atoms in the graph.
  	 * Complexity: O(m*n*maxBonds) m = number of bonds, n = number of atoms
  	 * @param mol
  	 * @param maxBonds
  	 * @return an array A[i][j] = nBonds if i and j are connected by less than maxBonds
  	 * 						      or -1  otherwise
  	 */
  	public static int[][] getNumberOfBondsBetweenAtoms(StereoMolecule mol, int maxBonds, int[][] dist) {
  		//Initialization of the data
  		if(dist==null)  dist = new int[mol.getAtoms()][mol.getAtoms()];
  		int N = dist.length;
  		for(int i=0; i<N; i++) {
  			dist[i][i] = 0;
  			for(int j=i+1; j<N; j++) {
  				dist[i][j] = dist[j][i] = -1;
  			}
  		}
  
  		//Algo: Dynamic Programming
  		for(int j=0; j<maxBonds; j++) { //Maximum of nBonds bonds
  			for(int i=0; i<mol.getBonds(); i++) {
  				int a1 = mol.getBondAtom(0, i);
  				int a2 = mol.getBondAtom(1, i);
  				if(a1>=N || a2>=N) continue;
  				for(int a0=0; a0<N; a0++) {
  					// Dynamic Programming: dist(a0,a2) = min(dist(a0, a2), dist(a0,a1)+1) [if dist(a0,a1)>0]
  					if(dist[a0][a1]>=0 && (dist[a0][a2]==-1 || dist[a0][a1]+1<dist[a0][a2]) && dist[a0][a1]<maxBonds) {
  						dist[a2][a0] = (dist[a0][a2] = (dist[a0][a1] + 1));
  					}
  					if(dist[a0][a2]>=0 && (dist[a0][a1]==-1 || dist[a0][a2]+1<dist[a0][a1]) && dist[a0][a2]<maxBonds) {
  						dist[a1][a0] = (dist[a0][a1] = (dist[a0][a2] + 1));
  					}
  				}
  			}
  		}
  		return dist;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param atm index of atom
  	 * @return false if atom is not C or if one of the neighbours is N, O, F, S, P or Cl.
  	 */
  	public static boolean isAliphaticAtom(StereoMolecule mol, int atm) {
  		boolean aliphatic = true;
  
  		if(mol.getAtomicNo(atm)!=6)
  			return false;
  
  		int nConn = mol.getAllConnAtoms(atm);
  
  		for (int i = 0; i < nConn; i++) {
  			int atmConn = mol.getConnAtom(atm, i);
  
  			int atomicNoConn = mol.getAtomicNo(atmConn);
  
  			if((atomicNoConn == 7) ||
  					(atomicNoConn == 8) ||
  					(atomicNoConn == 9) ||
  					(atomicNoConn == 15) ||
  					(atomicNoConn == 16) ||
  					(atomicNoConn == 17)) {
  				aliphatic = false;
  				break;
  			}
  		}
  
  		return aliphatic;
  	}
  
  	public static boolean isAcceptor(StereoMolecule mol, int atom) {
  		boolean acceptor = true;
  
  		if(mol.getAtomicNo(atom)!=8 && mol.getAtomicNo(atom)!=7)
  			return false;
  
  		return acceptor;
  	}
  
  	public static boolean isDonor(StereoMolecule mol, int atom) {
  		boolean donor = true;
  
  		if(mol.getAtomicNo(atom)!=8 && mol.getAtomicNo(atom)!=7)
  			return false;
  
  		if(mol.getAllHydrogens(atom)==0){
  			return false;
  		}
  
  		return donor;
  	}
  
  
  	public static boolean isCarbonTwoValencesMinimum(StereoMolecule mol) {
  		boolean twoValencesMin = true;
  		int atoms = mol.getAtoms();
  		for (int i = 0; i < atoms; i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			if(atomicNo==6){
  				int connected = mol.getConnAtoms(i);
  				if(connected < 2){
  					twoValencesMin = false;
  					break;
  				}
  			}
  		}
  		return twoValencesMin;
  	}
  
  	public static boolean isCarbonOnlyConnected2Hetero(StereoMolecule mol) {
  		boolean hetero = true;
  		int atoms = mol.getAtoms();
  		outer:
  		for (int i = 0; i < atoms; i++) {
  			int atomicNo = mol.getAtomicNo(i);
  			if(atomicNo==6){
  				int connected = mol.getConnAtoms(i);
  				for (int j = 0; j < connected; j++) {
  					int indexConnecetd = mol.getConnAtom(i, j);
  					if(mol.getAtomicNo(indexConnecetd)==6){
  						hetero = false;
  						break outer;
  					}
  				}
  				if(mol.isRingAtom(i)){
  					hetero = true;
  					break;
  				}
  			}
  		}
  		return hetero;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param atomIndex
  	 * @return true if at least one neighbour is a hetero atom.
  	 */
  	public static boolean isCarbonConnected2Hetero(StereoMolecule mol, int atomIndex) {
  		boolean hetero = false;
  		int atomicNo = mol.getAtomicNo(atomIndex);
  		if(atomicNo==6){
  			int connected = mol.getConnAtoms(atomIndex);
  			for (int j = 0; j < connected; j++) {
  				int indexConnected = mol.getConnAtom(atomIndex, j);
  				if(mol.getAtomicNo(indexConnected)!=6 && mol.getAtomicNo(indexConnected)!=1){
  					hetero = true;
  					break;
  				}
  			}
  		}
  		return hetero;
  	}
  	public static boolean isOnlyLinkingAtom(StereoMolecule mol, int atomIndex) {
  		if(mol.getFragments().length>1){
  			throw new RuntimeException("Molecule already fragmented!");
  		}
  		StereoMolecule m = new StereoMolecule(mol);
  		m.ensureHelperArrays(Molecule.cHelperRings);
  		m.deleteAtom(atomIndex);
  		m.ensureHelperArrays(Molecule.cHelperRings);
  		return (m.getFragments().length>1)?true:false;
  	}
  	/**
  	 *
  	 * @param mol
  	 * @param arrAtomIndex
  	 * @return true if one of the atoms from arrAtomIndex is connected to a hetero atom.
  	 */
  	public static boolean isConnected2Hetero(StereoMolecule mol, int [] arrAtomIndex) {
  		boolean hetero = false;
  		for (int atomIndex : arrAtomIndex) {
  			int connected = mol.getConnAtoms(atomIndex);
  			for (int j = 0; j < connected; j++) {
  				int indexConnected = mol.getConnAtom(atomIndex, j);
  				if(mol.getAtomicNo(indexConnected)!=6 && mol.getAtomicNo(indexConnected)!=1){
  					hetero = true;
  					break;
  				}
  			}
  		}
  		return hetero;
  	}
  
  	public static boolean isRingInMolecule(StereoMolecule mol) {
  		boolean ring = false;
  		int atoms = mol.getAtoms();
  		for (int i = 0; i < atoms; i++) {
  			if(mol.isRingAtom(i)){
  				ring = true;
  				break;
  			}
  		}
  		return ring;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @return true if all atoms are in a ring.
  	 */
  	public static boolean isRingExclusively(StereoMolecule mol) {
  		boolean ring = true;
  		int atoms = mol.getAtoms();
  		for (int i = 0; i < atoms; i++) {
  			if(!mol.isRingAtom(i)){
  				ring = false;
  				break;
  			}
  		}
  		return ring;
  	}
  
  	public static boolean containsFiveBindingCarbon(StereoMolecule mol) {
  
  		boolean five=false;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(mol.getAtomicNo(i)==6){
  				int sumBO=0;
  				for (int j = 0; j < mol.getConnAtoms(i); j++) {
  					int bo = mol.getConnBondOrder(i,j);
  					sumBO+=bo;
  				}
  				if(sumBO>4){
  					five=true;
  					break;
  				}
  			}
  		}
  
  		return five;
  	}
  	public static boolean containsFiveBindingNitrogenWithoutCharge(StereoMolecule mol) {
  		boolean fiveBindingNNoPositiveCharge=false;
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(mol.getAtomicNo(i)==5){
  				int sumBO=0;
  				for (int j = 0; j < mol.getConnAtoms(i); j++) {
  					int bo = mol.getConnBondOrder(i,j);
  					sumBO+=bo;
  				}
  				if(sumBO==5){
  					if(mol.getAtomCharge(i)!=1) {
  						fiveBindingNNoPositiveCharge = true;
  						break;
  					}
  				}
  			}
  		}
  
  		return fiveBindingNNoPositiveCharge;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param atom
  	 * @return true for cyano and iso-cyano.
  	 */
  	public static boolean isCyanoN(StereoMolecule mol, int atom) {
  		if(mol.getAtomicNo(atom)!= 7){
  			return false;
  		}
  		int nConn = mol.getConnAtoms(atom);
  		if(nConn != 1){
  			return false;
  		}
  		int indexConn = mol.getConnAtom(atom, 0);
  		int bond = mol.getBond(atom, indexConn);
  		if(mol.getBondOrder(bond) != 3){
  			return false;
  		}
  		return true;
  	}
  
  	public static boolean isThioEther(StereoMolecule mol, int atom) {
  		if(mol.getAtomicNo(atom)!= 16){
  			return false;
  		}
  		int nConn = mol.getConnAtoms(atom);
  		if(nConn != 2){
  			return false;
  		}
  		boolean thio = true;
  		for (int i = 0; i < nConn; i++) {
  			int indexConn = mol.getConnAtom(atom, i);
  			if(mol.getAtomicNo(indexConn) != 6){
  				thio=false;
  				break;
  			}
  		}
  		return thio;
  	}
  
  	public static boolean isWildcard(StereoMolecule mol, int atom) {
  		if(mol.getAtomicNo(atom) == PeriodicTable.ConnectionPoint){
  			return true;
  		}
  		return false;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param atom
  	 * @return true if atom is S and at let one attached atom is O.
  	 */
  	public static boolean isSulfoxyGroup(StereoMolecule mol, int atom) {
  		if(mol.getAtomicNo(atom)!= 16){
  			return false;
  		}
  		int nConn = mol.getConnAtoms(atom);
  		boolean oxy = false;
  		for (int i = 0; i < nConn; i++) {
  			int indexConn = mol.getConnAtom(atom, i);
  			if(mol.getAtomicNo(indexConn) == 8){
  				oxy=true;
  				break;
  			}
  		}
  		return oxy;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param indexAtCentral
  	 * @param arrIndexAt
  	 * @return true if no connected carbon atom is in arrIndexAt
  	 */
  	public static boolean isIsolatedCarbon(StereoMolecule mol, int indexAtCentral, int [] arrIndexAt){
  
  		boolean isolated=true;
  		int nConnected = mol.getConnAtoms(indexAtCentral);
  		boolean [] arrConnected = new boolean[mol.getAtoms()];
  
  		for (int i = 0; i < nConnected; i++) {
  			arrConnected[mol.getConnAtom(indexAtCentral,i)]=true;
  		}
  
  		for (int indexAt : arrIndexAt) {
  			if(!arrConnected[indexAt]){
  				continue;
  			}
  			if(mol.getAtomicNo(indexAt)==6){
  				isolated=false;
  				break;
  			}
  		}
  
  		return isolated;
  	}
  
  	public static int [] extractAromaticRing(StereoMolecule mol, int [] arrIndexAt){
  
  		RingCollection rc = mol.getRingSet();
  		boolean [] arrRingMemberMarker = new boolean[mol.getAtoms()];
  		int [] arrIndexAromaticRing = null;
  		for (int i = 0; i < rc.getSize(); i++) {
  			if(!rc.isAromatic(i)){
  				continue;
  			}
  			Arrays.fill(arrRingMemberMarker, false);
  			int [] arrRingAtoms = rc.getRingAtoms(i);
  			for (int indexRingAtom : arrRingAtoms) {
  				arrRingMemberMarker[indexRingAtom]=true;
  			}
  
  			int sum=0;
  			for (int indexAt : arrIndexAt) {
  				if(arrRingMemberMarker[indexAt]){
  					sum++;
  				}
  			}
  
  			if(sum==arrRingAtoms.length){
  				arrIndexAromaticRing=arrRingAtoms;
  				break;
  			}
  		}
  
  		return arrIndexAromaticRing;
  	}
  
  	/**
  	 * Counts cyano and iso-cyano
  	 * @param mol
  	 * @return
  	 */
  	public static int getNumCyanoGroups(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(isCyanoN(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  
  	public static int getTotalCharge(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			n+=mol.getAtomCharge(i);
  		}
  		return n;
  	}
  	public static int getNumAtomsCharged(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(mol.getAtomCharge(i)!=0){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumAlcoholicOxygen(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(isAlcoholicOxygen(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumThioEther(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(isThioEther(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumSulfOxyGroups(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(isSulfoxyGroup(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static int getNumWildcards(StereoMolecule mol){
  		int n = 0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(isWildcard(mol, i)){
  				n++;
  			}
  		}
  		return n;
  	}
  
  	public static boolean isAlcoholicOxygen(StereoMolecule mol, int atom) {
  		if (mol.getAtomicNo(atom) != 8 || mol.isAromaticAtom(atom))
  			return false;
  		int nConnected = mol.getConnAtoms(atom);
  		if(nConnected != 1){
  			return false;
  		}
  		int indConn = mol.getConnAtom(atom, 0);
  		if(mol.getAtomicNo(indConn) != 6){
  			return false;
  		}
  		if(AtomFunctionAnalyzer.getNegativeNeighbourCount(mol, indConn) > 1){
  			return false;
  		}
  		return true;
  	}
  
  	public static boolean isEtherOxygenAtAromatic(StereoMolecule mol, int atom) {
  		boolean aromaticEtherO = false;
  		if (mol.getAtomicNo(atom) != 8 || mol.isAromaticAtom(atom))
  			return false;
  		int nConnected = mol.getConnAtoms(atom);
  		if(nConnected != 2){
  			return false;
  		}
  		int indConn1 = mol.getConnAtom(atom, 0);
  		int indConn2 = mol.getConnAtom(atom, 1);
  		if(mol.isAromaticAtom(indConn1)){
  			aromaticEtherO = true;
  		} else if(mol.isAromaticAtom(indConn2)){
  			aromaticEtherO = true;
  		}
  		return aromaticEtherO;
  	}
  
  	public static boolean isCarboxyC(StereoMolecule mol, int atom) {
  		if (mol.getAtomicNo(atom) != 6 || mol.getAtomPi(atom) != 0)
  			return false;
  		boolean carboxyO = false;
  		boolean etherO = false;
  		for (int i=0; i<mol.getConnAtoms(atom); i++) {
  			int conn = mol.getConnAtom(atom, i);
  			if (mol.getAtomicNo(conn) == 6) {
  				int bond = mol.getBond(atom, conn);
  				int bo = mol.getBondOrder(bond);
  				if(bo==1){
  					etherO=true;
  				} else if(bo==2){
  					carboxyO=true;
  				}
  			}
  		}
  		boolean carboxyC=false;
  		if(carboxyO && etherO){
  			carboxyC=true;
  		}
  		return carboxyC;
  	}
  
  	public static int replaceAtoms(ExtendedMolecule [] arr, int atnoOrig,int atnoRpl) {
  		int cc=0;
  		for (int i = 0; i < arr.length; i++) {
  			cc+=replaceAtoms(arr[i], atnoOrig,atnoRpl);
  		}
  		return cc;
  	}
  
  	public static int replaceAtoms(ExtendedMolecule mol, int atnoOrig,int atnoRpl) {
  		int cc=0;
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			if(mol.getAtomicNo(i)==atnoOrig) {
  				mol.setAtomicNo(i, atnoRpl);
  				cc++;
  			}
  		}
  		return cc;
  	}
  	public static void createSkeleton(ExtendedMolecule mol) {
  		for (int i = 0; i < mol.getAllAtoms(); i++) {
  			mol.setAtomicNo(i, 6);
  		}
  	}
  
  	/**
  	 * Removes all molecules that are a substructure of one of the molecules in the input list.
  	 * @param liInput
  	 * @return
  	 */
  	public static final LinkedList<StereoMolecule> removeSubStructures(List<StereoMolecule> liInput){
  		SSSearcher sss = new SSSearcher();
  		LinkedList<StereoMolecule> li = new LinkedList<StereoMolecule>(liInput);
  		for (int i = li.size()-1; i >= 0; i--) {
  			StereoMolecule mol1 = li.get(i);
  			for (int j = 0; j < li.size(); j++) {
  				if(i!=j){
  					StereoMolecule mol2 = li.get(j);
  					if(mol1.getAtoms()<mol2.getAtoms()){
  						sss.setMol(mol1, mol2);
  						if(sss.isFragmentInMolecule()){
  							li.remove(i);
  							break;
  						}
  					}
  				}
  			}
  		}
  
  		return li;
  	}
  
  	/**
  	 * Deletes the substructures, if found, in the molecule.
  	 * Starts with the first structure in the list.
  	 * Helper arrays are ensured.
  	 * @param mol
  	 * @param liFragment
  	 * @return
  	 */
  	public static StereoMolecule removeSubstructuresFromMolecule(StereoMolecule mol, List<StereoMolecule> liFragment){
  		StereoMolecule molReduced = new StereoMolecule(mol);
  		molReduced.ensureHelperArrays(Molecule.cHelperRings);
  		SSSearcher sss = new SSSearcher();
  		HashSet<Integer> hsIndexMatchingAtoms = new HashSet<Integer>();
  		for (StereoMolecule frag : liFragment) {
  			sss.setMol(frag, molReduced);
  			if(sss.findFragmentInMolecule()>0){
  				List<int[]> liMatch = sss.getMatchList();
  				hsIndexMatchingAtoms.clear();
  				for (int[] arrMatch : liMatch) {
  					for (int i = 0; i < arrMatch.length; i++) {
  						hsIndexMatchingAtoms.add(arrMatch[i]);
  					}
  				}
  				int [] arrIndexMatchingAtomsUnique = ArrayUtilsCalc.toIntArray(hsIndexMatchingAtoms);
  				molReduced.deleteAtoms(arrIndexMatchingAtomsUnique);
  				molReduced.ensureHelperArrays(Molecule.cHelperRings);
  			}
  		}
  		return molReduced;
  	}
  
  	public static StereoMolecule removeSubstructureFromMolecule(StereoMolecule mol, StereoMolecule frag){
  		StereoMolecule molReduced = new StereoMolecule(mol);
  		molReduced.ensureHelperArrays(Molecule.cHelperRings);
  		SSSearcher sss = new SSSearcher();
  		sss.setMol(frag, molReduced);
  		if(sss.findFragmentInMolecule()>0){
  			List<int[]> liMatch = sss.getMatchList();
  			if(liMatch.size() > 1){
  				throw new RuntimeException("Fragment found more than once!");
  
  			} if(liMatch.size() == 1){
  				int [] arrMatchIndex = liMatch.get(0);
  				molReduced.deleteAtoms(arrMatchIndex);
  				molReduced.ensureHelperArrays(Molecule.cHelperRings);
  //				boolean [] arrMatchingAtom = new boolean [molReduced.getAtoms()];
  //
  //
  //
  //				for (int i = 0; i < arrMatchIndex.length; i++) {
  //					arrMatchingAtom[arrMatchIndex[i]]=true;
  //				}
  //
  //				int indexRGroup = 1;
  //
  //				for (int i = 0; i < arrMatchIndex.length; i++) {
  //
  //					int atMatch = arrMatchIndex[i];
  //
  //					int nConnected = molReduced.getConnAtoms(atMatch);
  //
  //					for (int j = 0; j < nConnected; j++) {
  //
  //						int atConnected = molReduced.getConnAtom(atMatch, j);
  //
  //						if(arrMatchingAtom[atConnected]){
  //							continue;
  //						}
  //
  //						int bond = molReduced.getBond(atMatch, nConnected);
  //
  //						molReduced.deleteBond(bond);
  //
  //						int atNoRGroup = getAtomicNoRGroup(indexRGroup++);
  //
  //						int indexAtRGroup = molReduced.addAtom(atNoRGroup);
  //
  //						molReduced.addBond(atConnected, indexAtRGroup, Molecule.cBondTypeSingle);
  //
  //						molReduced.ensureHelperArrays(Molecule.cHelperNeighbours);
  //
  //					}
  //
  //				}
  //
  			}
  		}
  		return molReduced;
  	}
  
  	public static StereoMolecule removeWildcards(StereoMolecule mol){
  		StereoMolecule molReduced = new StereoMolecule(mol);
  		molReduced.ensureHelperArrays(Molecule.cHelperRings);
  		for (int i = molReduced.getAtoms()-1; i >= 0; i--) {
  			if(molReduced.getAtomicNo(i)==0){
  				molReduced.deleteAtom(i);
  			}
  		}
  		molReduced.ensureHelperArrays(Molecule.cHelperRings);
  		return molReduced;
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param bond
  	 * @param minAtomsDistance minimum number bonds between the atoms.
  	 * @param maxSpanAtoms minAtomsDistance + maxSpanAtoms is the maximum number bonds between the atoms.
  	 */
  	public static void setAtomBridge(ExtendedMolecule mol, int bond, int minAtomsDistance, int maxSpanAtoms){
  		int queryFeatures = 0;
  		queryFeatures |= (minAtomsDistance << Molecule.cBondQFBridgeMinShift);
  		queryFeatures |= (maxSpanAtoms << Molecule.cBondQFBridgeSpanShift);
  		queryFeatures &= ~Molecule.cBondQFAllBondTypes;
  		mol.setBondType(bond, Molecule.cBondTypeSingle);
  		mol.setBondQueryFeature(bond, Molecule.cBondQFAllFeatures, false);
  		mol.setBondQueryFeature(bond, queryFeatures, true);
  		mol.adaptBondTypeToQueryFeatures(bond);
  	}
  
  	public static void setColorMCS2Molecule(StereoMolecule mol, StereoMolecule molMCS){
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			mol.setAtomColor(i, Molecule.cAtomColorNone);
  		}
  		SSSearcher sss = new SSSearcher();
  		sss.setMol(molMCS, mol);
  		if(sss.findFragmentInMolecule()>0){
  			List<int[]> liMatch = sss.getMatchList();
  			int [] arrIndexMatch = liMatch.get(0);
  			for (int i = 0; i < arrIndexMatch.length; i++) {
  				mol.setAtomColor(arrIndexMatch[i], Molecule.cAtomColorBlue);
  			}
  		}
  	}
  
  	public static void setColorMolecule(StereoMolecule mol, int [] arrIndexMatch){
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			mol.setAtomColor(i, Molecule.cAtomColorNone);
  		}
  		for (int i = 0; i < arrIndexMatch.length; i++) {
  			mol.setAtomColor(arrIndexMatch[i], Molecule.cAtomColorBlue);
  		}
  	}
  
  	/**
  	 *
  	 * @param mol
  	 * @param arrIndexBonds
  	 * @param color i.e. Molecule.cAtomColorBlue
  	 */
  	public static void setColorMoleculeFromBondIndex(StereoMolecule mol, int [] arrIndexBonds, int color){
  		for (int i = 0; i < arrIndexBonds.length; i++) {
  			final int indexAtom1 = mol.getBondAtom(0, arrIndexBonds[i]);
  			final int indexAtom2 = mol.getBondAtom(1, arrIndexBonds[i]);
  			mol.setAtomColor(indexAtom1, color);
  			mol.setAtomColor(indexAtom2, color);
  
  		}
  	}
  
  	public static void setCoordinatesNull(StereoMolecule mol){
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			mol.setAtomX(i,0);
  			mol.setAtomY(i,0);
  			mol.setAtomZ(i,0);
  		}
  	}
  
  	/**
  	 * Returns the colors vector for the substructure in mol.
  	 * @param mol
  	 * @param frag
  	 * @param atomColor Molecule.cAtomColor
  	 * @return
  	 */
  	public static String getColorVectorSubstructure(StereoMolecule mol, StereoMolecule frag, int atomColor){
  		SSSearcher sss = new SSSearcher();
  		sss.setMol(frag, mol);
  		HashSet<Integer> hsAtomIndex = new HashSet<Integer>();
  		if(sss.findFragmentInMolecule()>0){
  			List<int[]> liMatch = sss.getMatchList();
  			int[] arr = liMatch.get(0);
  			for (int i = 0; i < arr.length; i++) {
  				hsAtomIndex.add(arr[i]);
  			}
  
  //			for (int[] arr : liMatch) {
  //				for (int i = 0; i < arr.length; i++) {
  //					hsAtomIndex.add(arr[i]);
  //				}
  //			}
  		}
  		String sAtomColor = ExtendedMoleculeFunctions.getColorRecord(mol, hsAtomIndex, atomColor);
  		return sAtomColor;
  	}
  
  	public static float getSimilarity(StereoMolecule m1, StereoMolecule m2, DescriptorHandler dh){
  		Object d1 = dh.createDescriptor(m1);
  		Object d2 = dh.createDescriptor(m2);
  		return dh.getSimilarity(d1, d2);
  	}
  
  	/**
  	 * Taken from Thomas Sander SkeletonSpheres descriptor
  	 * @param mol
  	 * @param rootAtom atom index to start
  	 * @param depth so many spheres are taken
  	 * @return Fragment containing the spheres started at rootAtom
  	 */
  	public static StereoMolecule getSphere(StereoMolecule mol, int rootAtom, int depth){
  		mol.ensureHelperArrays(Molecule.cHelperRings);
  		StereoMolecule fragment = new StereoMolecule(mol.getAtoms(), mol.getBonds());
  		boolean[] atomMask = getSphereAtomMask(mol, rootAtom, depth);
  		mol.copyMoleculeByAtoms(fragment, atomMask, true, null);
  		return fragment;
  	}
  	public static StereoMolecule getSphereWithDummyAtom(StereoMolecule mol, int rootAtom, int depth, int atNoDummy){
  
  		mol.ensureHelperArrays(Molecule.cHelperRings);
  		StereoMolecule fragment = new StereoMolecule(mol.getAtoms(), mol.getBonds());
  
  		boolean[] atomMask = getSphereAtomMask(mol, rootAtom, depth);
  		// Atoms that contained a connection to an atom outside the fragment.
  		boolean[] atomMask4Dummies = new boolean[mol.getAtoms()];
  
  		for (int i = 0; i < mol.getAtoms(); i++) {
  			if(atomMask[i]){
  				int nConn = mol.getConnAtoms(i);
  				for (int j = 0; j < nConn; j++) {
  					int indConAt = mol.getConnAtom(i,j);
  					if(!atomMask[indConAt]){
  						atomMask4Dummies[i]=true;
  					}
  				}
  			}
  		}
  		int [] atomMap = new int[atomMask.length];
  		mol.copyMoleculeByAtoms(fragment, atomMask, true, atomMap);
  
  		int [] atomMapInvers = new int[atomMask.length];
  		for (int i = 0; i < atomMap.length; i++) {
  			if(atomMap[i]==-1)
  				continue;
  			atomMapInvers[atomMap[i]]=i;
  		}
  
  		for (int i = 0; i < atomMask4Dummies.length; i++) {
  			if(atomMask4Dummies[i]){
  				int indAtConn = atomMapInvers[i];
  				int indAtNew = fragment.addAtom(atNoDummy);
  				int bnd = fragment.addBond(indAtConn, indAtNew);
  				fragment.setBondType(bnd, Molecule.cBondTypeSingle);
  			}
  		}
  		fragment.ensureHelperArrays(Molecule.cHelperRings);
  		return fragment;
  	}
  	public static boolean [] getSphereAtomMask(StereoMolecule mol, int rootAtom, int depth){
  
  		mol.ensureHelperArrays(Molecule.cHelperRings);
  
  		int[] atomList = new int[mol.getAtoms()];
  		boolean[] atomMaskSphere = new boolean[mol.getAtoms()];
  		boolean[] atomMask = new boolean[mol.getAtoms()];
  
  		int min = 0;
  		int max = 0;
  
  		for (int sphere=0; sphere<depth && max<mol.getAtoms(); sphere++) {
  			if (max == 0) {
  				atomList[0] = rootAtom;
  				atomMaskSphere[rootAtom] = true;
  				max = 1;
  			}
  			else {
  				int newMax = max;
  				for (int i=min; i<max; i++) {
  					int atom = atomList[i];
  					for (int j=0; j<mol.getConnAtoms(atom); j++) {
  						int connAtom = mol.getConnAtom(atom, j);
  						if (!atomMaskSphere[connAtom]) {
  							atomMaskSphere[connAtom] = true;
  							atomList[newMax++] = connAtom;
  						}
  					}
  				}
  				min = max;
  				max = newMax;
  			}
  			for (int i = 0; i < atomMaskSphere.length; i++) {
  				if(atomMaskSphere[i])
  					atomMask[i]=true;
  			}
  		}
  		return atomMask;
  	}
  
  
  	/**
  	 * Starts with 1 and goes until 16
  	 * @return
  	 */
  	public static int getAtomicNoRGroup(int r){
  		if(r > arrRGroupsAtomicNo.length){
  			throw new RuntimeException("");
  		}
  		return arrRGroupsAtomicNo[r-1];
  	}
  
  	/**
  	 * Topological centaer atoms are the atoms with the lowest squared sum of topological distances to all atoms.
  	 * @param
  	 * @return
  	 */
  	public final static List<Integer> getTopologicalCenter(int [][] arrTopoDist) {
  		List<Integer> liTopoCenterAtoms = new ArrayList<>();
  		List<IntegerDouble> li = new ArrayList<>();
  		for (int i = 0; i < arrTopoDist.length; i++) {
  			double sum=0;
  			for (int j = 0; j < arrTopoDist.length; j++) {
  				sum += arrTopoDist[i][j]*arrTopoDist[i][j];
  			}
  			li.add(new IntegerDouble(i,sum));
  		}
  		Collections.sort(li, IntegerDouble.getComparatorDouble());
  		liTopoCenterAtoms.add(li.get(0).getInt());
  		for (int i = 1; i < li.size(); i++) {
  			if(li.get(i).getDouble()==li.get(0).getDouble()){
  				liTopoCenterAtoms.add(li.get(i).getInt());
  			}
  		}
  		return liTopoCenterAtoms;
  	}
  
  	public final static List<Integer> getTopologicalCenter(int [][] arrTopoDist, ListWithIntVec ilIndexAtoms) {
  		List<Integer> liTopoCenterAtoms = new ArrayList<Integer>();
  
  		List<IntegerDouble> li = new ArrayList<IntegerDouble>();
  
  		for (int i = 0; i < ilIndexAtoms.size(); i++) {
  
  			int indexAt1 = ilIndexAtoms.get(i);
  
  			double sum=0;
  			for (int j = 0; j < ilIndexAtoms.size(); j++) {
  
  				int indexAt2 = ilIndexAtoms.get(j);
  
  				sum += arrTopoDist[indexAt1][indexAt2]*arrTopoDist[indexAt1][indexAt2];
  			}
  
  			li.add(new IntegerDouble(indexAt1,sum));
  		}
  
  		Collections.sort(li, IntegerDouble.getComparatorDouble());
  
  		liTopoCenterAtoms.add(li.get(0).getInt());
  
  		for (int i = 1; i < li.size(); i++) {
  			if(li.get(i).getDouble()==li.get(0).getDouble()){
  				liTopoCenterAtoms.add(li.get(i).getInt());
  			}
  		}
  
  		return liTopoCenterAtoms;
  	}
  
  	/**
  	 * Gets the points with the maximum sum of squared topological distances to all atoms.
  
  	 * @param arrTopoDist
  	 * @return
  	 */
  	public final static List<Integer> getPeriphericPoints(int [][] arrTopoDist) {
  		List<Integer> liPeripheric = new ArrayList<>();
  		List<IntegerDouble> li = new ArrayList<>();
  		for (int i = 0; i < arrTopoDist.length; i++) {
  			double sum=0;
  			for (int j = 0; j < arrTopoDist.length; j++) {
  				sum += arrTopoDist[i][j]*arrTopoDist[i][j];
  			}
  			li.add(new IntegerDouble(i,sum));
  		}
  		Collections.sort(li, IntegerDouble.getComparatorDouble());
  		Collections.reverse(li);
  		liPeripheric.add(li.get(0).getInt());
  		for (int i = 1; i < li.size(); i++) {
  			if(li.get(i).getDouble()==li.get(0).getDouble()){
  				liPeripheric.add(li.get(i).getInt());
  			}
  		}
  		return liPeripheric;
  	}
  
  	/**
  	 * Distance is the sum of squares for every atom index.
  	 * Sorted in ascending order.
  	 * @param arrTopoDist
  	 * @return
  	 */
  	public final static List<IntegerDouble> getAllIndicesSortedByDistance(int [][] arrTopoDist) {
  		List<IntegerDouble> li = new ArrayList<>();
  		for (int i = 0; i < arrTopoDist.length; i++) {
  			double sum=0;
  			for (int j = 0; j < arrTopoDist.length; j++) {
  				sum += arrTopoDist[i][j]*arrTopoDist[i][j];
  			}
  			li.add(new IntegerDouble(i,sum));
  		}
  		Collections.sort(li, IntegerDouble.getComparatorDouble());
  		return li;
  	}
  
  	public final static String toStringCoordinates(StereoMolecule m){
  
  		StringBuilder sb = new StringBuilder();
  		for (int i = 0; i < m.getAllAtoms(); i++) {
  			if(i>0)
  				sb.append("\n");
  			sb.append(Formatter.format2(m.getAtomX(i)));
  			sb.append(", ");
  			sb.append(Formatter.format2(m.getAtomY(i)));
  			sb.append(", ");
  			sb.append(Formatter.format2(m.getAtomZ(i)));
  		}
  		return sb.toString();
  	}
  }
  