private Map<IAtom, List<String>> labelAtomsBySymbol(IAtomContainer atomCont) {
    Map<IAtom, List<String>> label_list = new HashMap<>();

    for (int i = 0; i < atomCont.getAtomCount(); i++) {
      List<String> label = new ArrayList<>(7);
      for (int a = 0; a < 7; a++) {
        label.add(a, "Z9");
      }

      IAtom refAtom = atomCont.getAtom(i);
      /*
       * Important Step: Discriminate between source atom types
       */
      String referenceAtom;
      if (refAtom instanceof IQueryAtom) {
        referenceAtom =
            ((IQueryAtom) refAtom).getSymbol() == null ? "*" : ((IQueryAtom) refAtom).getSymbol();
        //                System.out.println("referenceAtom " + referenceAtom);
      } else if (!(refAtom instanceof IQueryAtom) && this.matchAtomType) {
        referenceAtom =
            refAtom.getAtomTypeName() == null ? refAtom.getSymbol() : refAtom.getAtomTypeName();
      } else {
        referenceAtom = refAtom.getSymbol();
      }
      label.set(0, referenceAtom);
      List<IAtom> connAtoms = atomCont.getConnectedAtomsList(refAtom);

      int counter = 1;

      for (IAtom negAtom : connAtoms) {
        String neighbouringAtom;
        if (refAtom instanceof IQueryAtom) {
          neighbouringAtom =
              ((IQueryAtom) negAtom).getSymbol() == null ? "*" : ((IQueryAtom) negAtom).getSymbol();
          //                    System.out.println("neighbouringAtom " + neighbouringAtom);
        } else if (!(negAtom instanceof IQueryAtom) && this.matchAtomType) {
          neighbouringAtom =
              negAtom.getAtomTypeName() == null ? negAtom.getSymbol() : negAtom.getAtomTypeName();
        } else {
          neighbouringAtom = negAtom.getSymbol();
        }
        label.set(counter, neighbouringAtom);
        counter += 1;
      }
      //            System.out.println("label " + label);
      bubbleSort(label);
      label_list.put(refAtom, label);
    }
    return label_list;
  }
  @Test
  public void testSybylAtomTypePerceptionBenzene()
      throws CDKException, FileNotFoundException, IOException, BioclipseException, CoreException,
          InvocationTargetException {

    IAtomContainer ac = MoleculeFactory.makeBenzene();

    ICDKMolecule mol = new CDKMolecule(ac);

    debug.perceiveSybylAtomTypes(mol);

    System.out.println("** BENZENE **");

    System.out.println(AtomContainerDiff.diff(ac, mol.getAtomContainer()));

    for (int i = 0; i < mol.getAtomContainer().getAtomCount(); i++) {
      IAtom a = mol.getAtomContainer().getAtom(i);
      System.out.println("Atom: " + a.getSymbol() + i + ", type=" + a.getAtomTypeName());
    }

    assertEquals("C.ar", mol.getAtomContainer().getAtom(0).getAtomTypeName());
    assertEquals("C.ar", mol.getAtomContainer().getAtom(1).getAtomTypeName());
    assertEquals("C.ar", mol.getAtomContainer().getAtom(2).getAtomTypeName());
    assertEquals("C.ar", mol.getAtomContainer().getAtom(3).getAtomTypeName());
    assertEquals("C.ar", mol.getAtomContainer().getAtom(4).getAtomTypeName());
    assertEquals("C.ar", mol.getAtomContainer().getAtom(5).getAtomTypeName());
  }
  /** Converts an Atom to a MSML Atom element */
  protected AtomType convertAtom(IAtom atom, String parentID) {
    AtomType atomElement = new AtomType();
    String id = "";
    if (atom.getID() != null) {
      id = atom.getID();
    } else {
      id = Integer.toString(atom.hashCode());
    }
    String atomID = parentID + PREFIX_ATOM + id;
    atomElement.setId(atomID);
    atomElement.setCustomId("" + id);
    // atom name
    atomElement.setTitle(atom.getAtomTypeName());
    // element name
    atomElement.setElementType(atom.getSymbol());
    double x, y, z;
    // set choords
    if (atom.getPoint3d() != null) {
      x = atom.getPoint3d().x;
      y = atom.getPoint3d().y;
      z = atom.getPoint3d().z;
    } else { // what if mol in 2d? -> has to use getPoint2d
      x = atom.getPoint2d().x;
      y = atom.getPoint2d().y;
      z = 0.0;
    }
    if (atom.getFormalCharge() != null) {
      atomElement.setFormalCharge(BigInteger.valueOf(atom.getFormalCharge()));
    }
    atomElement.setX3(new Float(x));
    atomElement.setY3(new Float(y));
    atomElement.setZ3(new Float(z));

    return atomElement;
  }
  /**
   * This method calculates the ionization potential of an atom.
   *
   * @param atom The IAtom to ionize.
   * @param container Parameter is the IAtomContainer.
   * @return The ionization potential. Not possible the ionization.
   */
  @Override
  public DescriptorValue calculate(IAtom atom, IAtomContainer container) {
    double value = 0;
    // FIXME: for now I'll cache a few modified atomic properties, and restore them at the end of
    // this method
    String originalAtomtypeName = atom.getAtomTypeName();
    Integer originalNeighborCount = atom.getFormalNeighbourCount();
    Integer originalValency = atom.getValency();
    IAtomType.Hybridization originalHybrid = atom.getHybridization();
    Double originalBondOrderSum = atom.getBondOrderSum();
    Order originalMaxBondOrder = atom.getMaxBondOrder();

    if (!isCachedAtomContainer(container)) {
      try {
        AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(container);

        LonePairElectronChecker lpcheck = new LonePairElectronChecker();
        lpcheck.saturate(container);
      } catch (CDKException e) {
        return new DescriptorValue(
            getSpecification(),
            getParameterNames(),
            getParameters(),
            new DoubleResult(Double.NaN),
            getDescriptorNames(),
            e);
      }
    }

    try {
      value = IonizationPotentialTool.predictIP(container, atom);
    } catch (CDKException e) {
      return new DescriptorValue(
          getSpecification(),
          getParameterNames(),
          getParameters(),
          new DoubleResult(Double.NaN),
          getDescriptorNames(),
          e);
    }
    // restore original props
    atom.setAtomTypeName(originalAtomtypeName);
    atom.setFormalNeighbourCount(originalNeighborCount);
    atom.setValency(originalValency);
    atom.setHybridization(originalHybrid);
    atom.setMaxBondOrder(originalMaxBondOrder);
    atom.setBondOrderSum(originalBondOrderSum);

    return new DescriptorValue(
        getSpecification(),
        getParameterNames(),
        getParameters(),
        new DoubleResult(value),
        getDescriptorNames());
  }
 /** Tests if the electron count matches the H&uuml;ckel 4n+2 rule. */
 private static boolean isHueckelValid(IAtomContainer singleRing) throws CDKException {
   int electronCount = 0;
   for (IAtom ringAtom : singleRing.atoms()) {
     if (ringAtom.getHybridization() != CDKConstants.UNSET
             && (ringAtom.getHybridization() == Hybridization.SP2)
         || ringAtom.getHybridization() == Hybridization.PLANAR3) {
       // for example, a carbon
       // note: the double bond is in the ring, that has been tested earlier
       // FIXME: this does assume bond orders to be resolved too, when detecting
       // sprouting double bonds
       if ("N.planar3".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("N.minus.planar3".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("N.amide".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("S.2".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("S.planar3".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("C.minus.planar".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("O.planar3".equals(ringAtom.getAtomTypeName())) {
         electronCount += 2;
       } else if ("N.sp2.3".equals(ringAtom.getAtomTypeName())) {
         electronCount += 1;
       } else {
         if (factory == null) {
           factory =
               AtomTypeFactory.getInstance(
                   "org/openscience/cdk/dict/data/cdk-atom-types.owl", ringAtom.getBuilder());
         }
         IAtomType type = factory.getAtomType(ringAtom.getAtomTypeName());
         Object property = type.getProperty(CDKConstants.PI_BOND_COUNT);
         if (property != null && property instanceof Integer) {
           electronCount += ((Integer) property).intValue();
         }
       }
     } else if (ringAtom.getHybridization() != null
         && ringAtom.getHybridization() == Hybridization.SP3
         && getLonePairCount(ringAtom) > 0) {
       // for example, a nitrogen or oxygen
       electronCount += 2;
     }
   }
   return (electronCount % 4 == 2) && (electronCount > 2);
 }
  @Test
  public void testSybylAtomTypePerceptionFromSMILES()
      throws FileNotFoundException, IOException, BioclipseException, CoreException,
          InvocationTargetException {

    ICDKMolecule mol = cdk.fromSMILES("C1CCCCC1CCOC");

    debug.perceiveSybylAtomTypes(mol);

    for (int i = 0; i < mol.getAtomContainer().getAtomCount(); i++) {
      IAtom a = mol.getAtomContainer().getAtom(i);
      System.out.println("Atom: " + a.getSymbol() + i + ", type=" + a.getAtomTypeName());
    }
  }
  @Test
  public void testSybylAtomTypePerception()
      throws FileNotFoundException, IOException, BioclipseException, CoreException,
          InvocationTargetException {

    String path = getClass().getResource("/testFiles/atp.mol").getPath();
    ICDKMolecule mol = cdk.loadMolecule(new MockIFile(path));

    System.out.println("mol: " + mol.toString());

    debug.perceiveSybylAtomTypes(mol);

    for (int i = 0; i < mol.getAtomContainer().getAtomCount(); i++) {
      IAtom a = mol.getAtomContainer().getAtom(i);
      System.out.println("Atom: " + a.getSymbol() + i + ", type=" + a.getAtomTypeName());
    }
  }
 /**
  * Determines if the isolatedRingSystem has attached double bonds, which are not part of the ring
  * system itself, and not part of any other ring system. Exceptions: a N.sp2.3 nitrogen with a
  * double ring to an oxygen outwards.
  */
 private static boolean isRingSystemSproutedWithNonRingDoubleBonds(
     IAtomContainer fullContainer, IAtomContainer isolatedRingSystem) {
   Iterator<IAtom> atoms = isolatedRingSystem.atoms().iterator();
   while (atoms.hasNext()) {
     IAtom atom = atoms.next();
     Iterator<IBond> neighborBonds = fullContainer.getConnectedBondsList(atom).iterator();
     while (neighborBonds.hasNext()) {
       IBond neighborBond = neighborBonds.next();
       if (!neighborBond.getFlag(CDKConstants.ISINRING)
               && neighborBond.getOrder() == CDKConstants.BONDORDER_DOUBLE
           || neighborBond.getOrder() == CDKConstants.BONDORDER_TRIPLE) {
         if (!("N.sp2.3".equals(atom.getAtomTypeName())
             && "O.sp2".equals(neighborBond.getConnectedAtom(atom).getAtomTypeName())))
           return true;
       }
     }
   }
   return false;
 }
    public String perceiveSybylAtomTypes(IMolecule mol)
                        throws InvocationTargetException {
        
        ICDKMolecule cdkmol;
        
        try {
            cdkmol = cdk.asCDKMolecule(mol);
        } 
        catch (BioclipseException e) {
            System.out.println("Error converting cdk10 to cdk");
            e.printStackTrace();
            throw new InvocationTargetException(e);
        }
        
        IAtomContainer ac = cdkmol.getAtomContainer();
        CDKAtomTypeMatcher cdkMatcher 
            = CDKAtomTypeMatcher.getInstance(ac.getBuilder());
        AtomTypeMapper mapper 
            = AtomTypeMapper.getInstance(
                 "org/openscience/cdk/dict/data/cdk-sybyl-mappings.owl" );

        IAtomType[] sybylTypes = new IAtomType[ac.getAtomCount()];
        
        int atomCounter = 0;
        int a=0;
        for (IAtom atom : ac.atoms()) {
            IAtomType type;
            try {
                type = cdkMatcher.findMatchingAtomType(ac, atom);
            } 
            catch (CDKException e) {
                type = null;
            }
            if (type==null) {
//                logger.debug("AT null for atom: " + atom);
                type = atom.getBuilder().newAtomType(atom.getSymbol());
                type.setAtomTypeName("X");
            }
            AtomTypeManipulator.configure(atom, type);
            a++;
        }
        try {
            CDKHueckelAromaticityDetector.detectAromaticity(ac);
//            System.out.println("Arom: " 
//                + CDKHueckelAromaticityDetector.detectAromaticity(ac) );
		    } 
        catch (CDKException e) {
			    logger.debug("Failed to perceive aromaticity: " + e.getMessage());
		    }
        for (IAtom atom : ac.atoms()) {
            String mappedType = mapper.mapAtomType(atom.getAtomTypeName());
            if ("C.2".equals(mappedType)
                    && atom.getFlag(CDKConstants.ISAROMATIC)) {
                mappedType = "C.ar";
            } 
            else if ("N.pl3".equals(mappedType)
                    && atom.getFlag(CDKConstants.ISAROMATIC)) {
                mappedType = "N.ar";
            }
            try {
                sybylTypes[atomCounter] = factory.getAtomType(mappedType);
		        } 
            catch (NoSuchAtomTypeException e) {
                // yes, setting null's here is important
                sybylTypes[atomCounter] = null; 
			      }
            atomCounter++;
        }
        StringBuffer result = new StringBuffer();
        // now that full perception is finished, we can set atom type names:
        for (int i = 0; i < sybylTypes.length; i++) {
            if (sybylTypes[i] != null) {
                ac.getAtom(i).setAtomTypeName(sybylTypes[i].getAtomTypeName());
            } 
            else {
                ac.getAtom(i).setAtomTypeName("X");
            }
            
            result.append(i).append(':').append(ac.getAtom(i).getAtomTypeName())
                  /*.append("\n")*/;

        }
        return result.toString();
    }
  /**
   * @return
   * @throws Exception
   */
  public int process() throws Exception {
    if (file == null) throw new Exception("File not assigned! Use -f command line option.");
    if (!file.exists()) throw new FileNotFoundException(file.getAbsolutePath());
    int records_read = 0;
    int records_processed = 0;
    int records_error = 0;

    InputStream in = new FileInputStream(file);
    /**
     * cdk-io module http://ambit.uni-plovdiv.bg:8083/nexus/index.html#nexus-
     * search;classname~IteratingMDLReader
     */
    IteratingSDFReader reader = null;

    SDFWriter writer = new SDFWriter(new OutputStreamWriter(System.out));

    try {

      reader = new IteratingSDFReader(in, DefaultChemObjectBuilder.getInstance());
      LOGGER.log(Level.INFO, String.format("Reading %s", file.getAbsoluteFile()));
      while (reader.hasNext()) {
        /** Note recent versions allow IAtomContainer molecule = reader.next(); */
        Object object = reader.next();
        IAtomContainer molecule = null;
        if (object instanceof IAtomContainer) molecule = (IAtomContainer) object;
        else break;

        records_read++;
        try {
          /** cdk-standard module */
          AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(molecule);
          // CDKHueckelAromaticityDetector.detectAromaticity(molecule);
          for (IAtom atom : molecule.atoms())
            if (atom.getImplicitHydrogenCount() == null) {
              LOGGER.fine(
                  atom.getSymbol()
                      + "\t"
                      + atom.getAtomTypeName()
                      + "\t"
                      + atom.getImplicitHydrogenCount());
              atom.setImplicitHydrogenCount(0);
            }

          molecule = AtomContainerManipulator.copyAndSuppressedHydrogens(molecule);

          /** Generate SMILES and assign as properties */
          assignSMILES(molecule);
          molecule.setProperty("REACTION", "REACTANT");
          molecule.setProperty("SMIRKS", "");
          /** Apply reactions */
          writer.write(molecule);
          for (int r = 0; r < smirks.length; r++) {
            if (reactions[r] == null) {
              reactions[r] = smrkMan.parse(smirks[r][1]);
            }
            IAtomContainer reactant = molecule.clone();
            if (smrkMan.applyTransformation(reactant, reactions[r])) {
              AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(reactant);
              reactant.setProperty("REACTION", "PRODUCT OF " + smirks[r][0]);
              reactant.setProperty("SMIRKS", smirks[r][1]);
              try {
                assignSMILES(reactant);
              } catch (Exception x) {
                LOGGER.log(Level.WARNING, x.getMessage());
              }
              writer.write(reactant);
            }
          }
          records_processed++;
          ;
        } catch (Exception x) {
          System.err.println("*");
          records_error++;
          LOGGER.log(
              Level.SEVERE,
              String.format("[Record %d] Error %s\n", records_read, file.getAbsoluteFile()),
              x);
        }
      }
    } catch (Exception x) {
      LOGGER.log(
          Level.SEVERE,
          String.format("[Record %d] Error %s\n", records_read, file.getAbsoluteFile()),
          x);
    } finally {
      try {
        reader.close();
      } catch (Exception x) {
      }
      try {
        writer.close();
      } catch (Exception x) {
      }
    }
    LOGGER.log(
        Level.INFO,
        String.format(
            "[Records read/processed/error %d/%d/%d] %s",
            records_read, records_processed, records_error, file.getAbsoluteFile()));
    return records_read;
  }