context: /* 
   * Copyright (C) 2003-2007  The Chemistry Development Kit (CDK) project
   *                    2014  Mark B Vine (orcid:0000-0002-7794-0426)
   *
   * Contact: cdk-devel@lists.sourceforge.net
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public License
   * as published by the Free Software Foundation; either version 2.1
   * of the License, or (at your option) any later version.
   * All we ask is that proper credit is given for our work, which includes
   * - but is not limited to - adding the above copyright notice to the beginning
   * of your source code files, and to any copyright notice that you may distribute
   * with programs based on this work.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT Any WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   */
  
  using NCDK.AtomTypes;
  using NCDK.Config;
  using NCDK.Graphs;
  using NCDK.RingSearches;
  using NCDK.Sgroups;
  using NCDK.Stereo;
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  
  namespace NCDK.Tools.Manipulator
  {
      public enum MolecularWeightTypes
      {
          /// <summary>
          /// For use with <see cref="AtomContainerManipulator.GetMass(IAtomContainer, MolecularWeightTypes)"/>. This option uses the mass
          /// stored on atoms (<see cref="IIsotope.ExactMass"/>) or the average mass of the
          /// element when unspecified. 
          /// </summary>
          MolWeight = 0x1,
  
          /// <summary>
          /// For use with <see cref="AtomContainerManipulator.GetMass(IAtomContainer, MolecularWeightTypes)"/>. This option ignores the
          /// mass stored on atoms (<see cref="IIsotope.ExactMass"/>) and uses the average
          /// mass of each element. This option is primarily provided for backwards
          /// compatibility.
          /// </summary>
          MolWeightIgnoreSpecified = 0x2,
  
          /// <summary>
          /// For use with <see cref="AtomContainerManipulator.GetMass(IAtomContainer, MolecularWeightTypes)"/>. This option uses the mass
          /// stored on atoms <see cref="IIsotope.ExactMass"/> or the mass of the major
          /// isotope when this is not specified.
          /// </summary>
          MonoIsotopic = 0x3,
  
          /// <summary>
          /// For use with <see cref="AtomContainerManipulator.GetMass(IAtomContainer, MolecularWeightTypes)"/>. This option uses the mass
          /// stored on atoms <see cref="IIsotope.ExactMass"/> and then calculates a
          /// distribution for any unspecified atoms and uses the most abundant
          /// distribution. For example C<sub>6</sub>Br<sub>6</sub> would have three
          /// <sup>79</sup>Br and <sup>81</sup>Br because their abundance is 51 and
          /// 49%.
          /// </summary>
          MostAbundant = 0x4,
      }
  
      /// <summary>
      /// Class with convenience methods that provide methods to manipulate
      /// AtomContainer's. 
      /// </summary>
      /// <example>
      /// For example:
      /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Tools.AtomContainerManipulator_Example.cs+1"]/*' />
      /// will replace the Atom in the AtomContainer, but in all the ElectronContainer's
      /// it participates too.
      /// </example>
      // @cdk.module standard
      // @author  Egon Willighagen
      // @cdk.created 2003-08-07
      public static class AtomContainerManipulator
      {
          /// <summary>
          /// Extract a substructure from an atom container, in the form of a new
          /// cloned atom container with only the atoms with indices in atomIndices and
          /// bonds that connect these atoms.
          /// </summary>
          /// <remarks>
          /// Note that this may result in a disconnected atom container.
          /// </remarks>
          /// <param name="atomContainer">the source container to extract from</param>
          /// <param name="atomIndices">the indices of the substructure</param>
          /// <returns>a cloned atom container with a substructure of the source</returns>
          public static IAtomContainer ExtractSubstructure(IAtomContainer atomContainer, params int[] atomIndices)
          {
              var substructure = (IAtomContainer)atomContainer.Clone();
              int numberOfAtoms = substructure.Atoms.Count;
              var atoms = new IAtom[numberOfAtoms];
              for (int atomIndex = 0; atomIndex < numberOfAtoms; atomIndex++)
              {
                  atoms[atomIndex] = substructure.Atoms[atomIndex];
              }
              Array.Sort(atomIndices);
              for (int index = 0; index < numberOfAtoms; index++)
              {
                  if (Array.BinarySearch(atomIndices, index) < 0)
                  {
                      var atom = atoms[index];
                      substructure.RemoveAtom(atom);
                  }
              }
  
              return substructure;
          }
  
          /// <summary>
          /// Returns an atom in an atom container identified by id
          /// </summary>
          /// <param name="ac">The atom container to search in</param>
          /// <param name="id">The id to search for</param>
          /// <returns>An atom having <paramref name="id"/></returns>
          /// <exception cref="CDKException">There is no such atom</exception>
          public static IAtom GetAtomById(IAtomContainer ac, string id)
          {
              for (int i = 0; i < ac.Atoms.Count; i++)
              {
                  if (ac.Atoms[i].Id != null && string.Equals(ac.Atoms[i].Id, id, StringComparison.Ordinal))
                      return ac.Atoms[i];
              }
              throw new CDKException("no suc atom");
          }
  
          /// <summary>
          /// Substitute one atom in a container for another adjusting bonds, single electrons, lone pairs, and stereochemistry
          /// as required.
          /// </summary>
          /// <param name="container">the container to replace the atom of</param>
          /// <param name="oldAtom">the atom to replace</param>
          /// <param name="newAtom">the atom to insert</param>
          /// <returns>whether replacement was made</returns>
          public static bool ReplaceAtomByAtom(IAtomContainer container, IAtom oldAtom, IAtom newAtom)
          {
              if (oldAtom == null)
                  throw new ArgumentNullException(nameof(oldAtom), "Atom to be replaced was null!");
              var idx = container.Atoms.IndexOf(oldAtom);
              if (idx < 0)
                  return false;
              container.Atoms[idx] = newAtom ?? throw new ArgumentNullException(nameof(newAtom), "Replacement atom was null!");
              var sgrougs = container.GetCtabSgroups();
              if (sgrougs != null)
              {
                  bool updated = false;
                  var replaced = new List<Sgroup>();
                  foreach (var org in sgrougs)
                  {
                      if (org.Atoms.Contains(oldAtom))
                      {
                          updated = true;
                          var cpy = new Sgroup();
                          foreach (var atom in org.Atoms)
                          {
                              if (!oldAtom.Equals(atom))
                                  cpy.Atoms.Add(atom);
                              else
                                  cpy.Atoms.Add(newAtom);
                          }
                          foreach (var bond in org.Bonds)
                              cpy.Bonds.Add(bond);
                          foreach (var parent in org.Parents)
                              cpy.AddParent(parent);
                          foreach (var key in org.AttributeKeys)
                              cpy.PutValue(key, org.GetValue(key));
                          replaced.Add(cpy);
                      }
                      else
                      {
                          replaced.Add(org);
                      }
                  }
                  if (updated)
                  {
                      container.SetCtabSgroups(replaced);
                  }
              }
  
              return true;
          }
  
          /// <summary>
          /// Get the summed charge of all atoms in an AtomContainer
          /// </summary>
          /// <param name="atomContainer">The IAtomContainer to manipulate</param>
          /// <returns>The summed charges of all atoms in this AtomContainer.</returns>
          public static double GetTotalCharge(IAtomContainer atomContainer)
          {
              double charge = 0.0;
              foreach (var atom in atomContainer.Atoms)
              {
                  // we assume CDKConstant.Unset is equal to 0
                  var thisCharge = atom.Charge;
                  if (thisCharge.HasValue)
                      charge += thisCharge.Value;
              }
              return charge;
          }
  
          private static bool HasIsotopeSpecified(IIsotope atom)
          {
              return (atom.MassNumber ?? 0) != 0;
          }
  
          private static double GetExactMass(IsotopeFactory isofact, IIsotope atom)
          {
              if (atom.ExactMass != null)
                  return atom.ExactMass.Value;
  
gt:             else if (atom.MassNumber != null)