Fixes #3998 (rdkit/rdkit#4248)

In order to fix this problem I needed to add a function which kekulizes a fragment of a molecule instead of the whole thing.

That new function is currently in the `MolOps::details` namespace since I really don't think it should be used from other code until we've tested it more thoroughly and are sure we're happy with the behavior.

MolFragmentToSmiles with kekuleSmiles=True raises AtomKekulizeException

**Describe the bug**
Release 2021_03_1 raises `AtomKekulizeException` by the following code.

**To Reproduce**
```python
from rdkit import Chem
mol = Chem.MolFromSmiles("CC(c1ccccc1)")
Chem.MolFragmentToSmiles(mol, [0], kekuleSmiles=False)
Chem.MolFragmentToSmiles(mol, [2,3,4,5,6,7], kekuleSmiles=False)
Chem.MolFragmentToSmiles(mol, [0], kekuleSmiles=True)  # Error is raised here
Chem.MolFragmentToSmiles(mol, [2,3,4,5,6,7], kekuleSmiles=True)
```

**Expected behavior**
```
'C'
'c1ccccc1'
'C'
'C1=CC=CC=C1'
```

**Screenshots**
```python
>>> import rdkit
>>> rdkit.__version__
'2021.03.1'
>>> from rdkit import Chem
>>> mol = Chem.MolFromSmiles("CC(c1ccccc1)")
>>> Chem.MolFragmentToSmiles(mol, [0], kekuleSmiles=False)
'C'
>>> Chem.MolFragmentToSmiles(mol, [2,3,4,5,6,7], kekuleSmiles=False)
'c1ccccc1'
>>> Chem.MolFragmentToSmiles(mol, [0], kekuleSmiles=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
rdkit.Chem.rdchem.AtomKekulizeException: non-ring atom 2 marked aromatic
>>> Chem.MolFragmentToSmiles(mol, [2,3,4,5,6,7], kekuleSmiles=True)
'C1=CC=CC=C1'
```

**Configuration (please complete the following information):**
 - RDKit version: 2021.03.1
 - OS: Windows 10
 - Python version (if relevant): 3.8.5
 - Are you using conda? : Yes
 - If you are using conda, which channel did you install the rdkit from?: conda-forge
 - If you are not using conda: how did you install the RDKit? : conda install rdkit==2021.03.1

**Additional context**
The result with Release_2020_09_5 is below.
```python
>>> import rdkit
>>> rdkit.__version__
'2020.09.5'
>>> from rdkit import Chem
>>> mol = Chem.MolFromSmiles("CC(c1ccccc1)")
>>> Chem.MolFragmentToSmiles(mol, [0], kekuleSmiles=False)
'C'
>>> Chem.MolFragmentToSmiles(mol, [2,3,4,5,6,7], kekuleSmiles=False)
'c1ccccc1'
>>> Chem.MolFragmentToSmiles(mol, [0], kekuleSmiles=True)
'C'
>>> Chem.MolFragmentToSmiles(mol, [2,3,4,5,6,7], kekuleSmiles=True)
'C1:C:C:C:C:C:1'
```

diff --git a/Code/GraphMol/Kekulize.cpp b/Code/GraphMol/Kekulize.cpp
index b20429ea16e..a4da0686cf4 100644
--- a/Code/GraphMol/Kekulize.cpp
+++ b/Code/GraphMol/Kekulize.cpp
@@ -495,28 +495,42 @@ void kekulizeFused(RWMol &mol, const VECT_INT_VECT &arings,
 }  // namespace

 namespace MolOps {
-void Kekulize(RWMol &mol, bool markAtomsBonds, unsigned int maxBackTracks) {
+namespace details {
+void KekulizeFragment(RWMol &mol, const boost::dynamic_bitset<> &atomsToUse,
+                      const boost::dynamic_bitset<> &bondsToUse,
+                      bool markAtomsBonds, unsigned int maxBackTracks) {
+  PRECONDITION(atomsToUse.size() == mol.getNumAtoms(),
+               "atomsToUse is wrong size");
+  PRECONDITION(bondsToUse.size() == mol.getNumBonds(),
+               "bondsToUse is wrong size");
+
   // there's no point doing kekulization if there are no aromatic bonds:
   bool foundAromatic = false;
-  for (ROMol::BondIterator bi = mol.beginBonds();
-       bi != mol.endBonds() && !foundAromatic; ++bi) {
-    if ((*bi)->getIsAromatic()) {
+  for (const auto bond : mol.bonds()) {
+    if (bondsToUse[bond->getIdx()] && bond->getIsAromatic()) {
       foundAromatic = true;
+      break;
     }
   }

   // before everything do implicit valence calculation and store them
   // we will repeat after kekulization and compare for the sake of error
   // checking
-  INT_VECT valences;
   int numAtoms = mol.getNumAtoms();
-  valences.reserve(numAtoms);
-  for (ROMol::AtomIterator ai = mol.beginAtoms(); ai != mol.endAtoms(); ++ai) {
-    (*ai)->calcImplicitValence(false);
-    valences.push_back((*ai)->getTotalValence());
-    if (!foundAromatic && (*ai)->getIsAromatic()) {
+  INT_VECT valences(numAtoms);
+  boost::dynamic_bitset<> dummyAts(mol.getNumAtoms());
+  for (auto atom : mol.atoms()) {
+    if (!atomsToUse[atom->getIdx()]) {
+      continue;
+    }
+    atom->calcImplicitValence(false);
+    valences[atom->getIdx()] = atom->getTotalValence();
+    if (atom->getIsAromatic()) {
       foundAromatic = true;
     }
+    if (!atom->getAtomicNum()) {
+      dummyAts[atom->getIdx()] = 1;
+    }
   }
   if (!foundAromatic) {
     return;
@@ -533,41 +547,46 @@ void Kekulize(RWMol &mol, bool markAtomsBonds, unsigned int maxBackTracks) {

   // first find the all the simple rings in the molecule that are not
   // completely composed of dummy atoms
-  VECT_INT_VECT arings;
-  boost::dynamic_bitset<> dummyAts(mol.getNumAtoms());
-  for (ROMol::AtomIterator atit = mol.beginAtoms(); atit != mol.endAtoms();
-       ++atit) {
-    if (!(*atit)->getAtomicNum()) {
-      dummyAts[(*atit)->getIdx()] = 1;
-    }
+  VECT_INT_VECT allrings;
+  if (mol.getRingInfo()->isInitialized()) {
+    allrings = mol.getRingInfo()->atomRings();
+  } else {
+    MolOps::findSSSR(mol, allrings);
   }
-  if (dummyAts.any()) {
-    VECT_INT_VECT allrings;
-    if (mol.getRingInfo()->isInitialized()) {
-      allrings = mol.getRingInfo()->atomRings();
-    } else {
-      MolOps::findSSSR(mol, allrings);
-    }
-    arings.reserve(allrings.size());
-    for (auto &ring : allrings) {
-      for (auto ai : ring) {
-        if (!dummyAts[ai]) {
-          arings.push_back(ring);
-          break;
-        }
+  VECT_INT_VECT arings;
+  arings.reserve(allrings.size());
+  for (auto &ring : allrings) {
+    bool ringOk = false;
+    for (auto ai : ring) {
+      if (!atomsToUse[ai]) {
+        ringOk = false;
+        break;
+      }
+      if (!dummyAts[ai]) {
+        ringOk = true;
       }
     }
-  } else {
-    if (mol.getRingInfo()->isInitialized()) {
-      arings = mol.getRingInfo()->atomRings();
-    } else {
-      MolOps::findSSSR(mol, arings);
+    if (ringOk) {
+      arings.push_back(ring);
     }
   }

+  VECT_INT_VECT allbrings;
+  RingUtils::convertToBonds(arings, allbrings, mol);
   VECT_INT_VECT brings;
-  // brings = mol.getRingInfo()->bondRings();
-  RingUtils::convertToBonds(arings, brings, mol);
+  brings.reserve(allbrings.size());
+  for (auto &ring : allbrings) {
+    bool ringOk = true;
+    for (auto bi : ring) {
+      if (!bondsToUse[bi]) {
+        ringOk = false;
+        break;
+      }
+    }
+    if (ringOk) {
+      brings.push_back(ring);
+    }
+  }

   // make a neighbor map for the rings i.e. a ring is a
   // neighbor to another candidate ring if it shares at least
@@ -602,28 +621,31 @@ void Kekulize(RWMol &mol, bool markAtomsBonds, unsigned int maxBackTracks) {
   if (markAtomsBonds) {
     // if we want the atoms and bonds to be marked non-aromatic do
     // that here.
-    for (ROMol::BondIterator bi = mol.beginBonds(); bi != mol.endBonds();
-         ++bi) {
-      (*bi)->setIsAromatic(false);
+    for (auto bond : mol.bonds()) {
+      if (bondsToUse[bond->getIdx()]) {
+        bond->setIsAromatic(false);
+      }
     }
-    for (ROMol::AtomIterator ai = mol.beginAtoms(); ai != mol.endAtoms();
-         ++ai) {
-      if ((*ai)->getIsAromatic()) {
-        if (!mol.getRingInfo()->numAtomRings((*ai)->getIdx())) {
+    for (auto atom : mol.atoms()) {
+      if (atomsToUse[atom->getIdx()] && atom->getIsAromatic()) {
+        // if we're doing the full molecule and there are aromatic atoms not in
+        // a ring, throw an exception
+        if (atomsToUse.all() && bondsToUse.all() &&
+            !mol.getRingInfo()->numAtomRings(atom->getIdx())) {
           std::ostringstream errout;
-          errout << "non-ring atom " << (*ai)->getIdx() << " marked aromatic";
+          errout << "non-ring atom " << atom->getIdx() << " marked aromatic";
           std::string msg = errout.str();
           BOOST_LOG(rdErrorLog) << msg << std::endl;
-          throw AtomKekulizeException(msg, (*ai)->getIdx());
+          throw AtomKekulizeException(msg, atom->getIdx());
         }
-        (*ai)->setIsAromatic(false);
+        atom->setIsAromatic(false);
         // make sure "explicit" Hs on things like pyrroles don't hang around
         // this was Github Issue 141
-        if (((*ai)->getAtomicNum() == 7 || (*ai)->getAtomicNum() == 15) &&
-            (*ai)->getFormalCharge() == 0 && (*ai)->getNumExplicitHs() == 1) {
-          (*ai)->setNoImplicit(false);
-          (*ai)->setNumExplicitHs(0);
-          (*ai)->updatePropertyCache(false);
+        if ((atom->getAtomicNum() == 7 || atom->getAtomicNum() == 15) &&
+            atom->getFormalCharge() == 0 && atom->getNumExplicitHs() == 1) {
+          atom->setNoImplicit(false);
+          atom->setNumExplicitHs(0);
+          atom->updatePropertyCache(false);
         }
       }
     }
@@ -632,19 +654,29 @@ void Kekulize(RWMol &mol, bool markAtomsBonds, unsigned int maxBackTracks) {
   // ok some error checking here force a implicit valence
   // calculation that should do some error checking by itself. In
   // addition compare them to what they were before kekulizing
-  int i = 0;
-  for (ROMol::AtomIterator ai = mol.beginAtoms(); ai != mol.endAtoms(); ++ai) {
-    int val = (*ai)->getTotalValence();
-    if (val != valences[i]) {
+  for (auto atom : mol.atoms()) {
+    if (!atomsToUse[atom->getIdx()]) {
+      continue;
+    }
+    int val = atom->getTotalValence();
+    if (val != valences[atom->getIdx()]) {
       std::ostringstream errout;
-      errout << "Kekulization somehow screwed up valence on " << (*ai)->getIdx()
-             << ": " << val << "!=" << valences[i] << std::endl;
+      errout << "Kekulization somehow screwed up valence on " << atom->getIdx()
+             << ": " << val << "!=" << valences[atom->getIdx()] << std::endl;
       std::string msg = errout.str();
       BOOST_LOG(rdErrorLog) << msg << std::endl;
-      throw AtomKekulizeException(msg, (*ai)->getIdx());
+      throw AtomKekulizeException(msg, atom->getIdx());
     }
-    i++;
   }
 }
-}  // end of namespace MolOps
-}  // end of namespace RDKit
+}  // namespace details
+void Kekulize(RWMol &mol, bool markAtomsBonds, unsigned int maxBackTracks) {
+  boost::dynamic_bitset<> atomsToUse(mol.getNumAtoms());
+  atomsToUse.set();
+  boost::dynamic_bitset<> bondsToUse(mol.getNumBonds());
+  bondsToUse.set();
+  details::KekulizeFragment(mol, atomsToUse, bondsToUse, markAtomsBonds,
+                            maxBackTracks);
+}
+}  // namespace MolOps
+}  // namespace RDKit
diff --git a/Code/GraphMol/MolOps.h b/Code/GraphMol/MolOps.h
index ecf07fdf7be..6a7bf3d7d9f 100644
--- a/Code/GraphMol/MolOps.h
+++ b/Code/GraphMol/MolOps.h
@@ -998,7 +998,15 @@ RDKIT_GRAPHMOL_EXPORT unsigned getNumAtomsWithDistinctProperty(
 //! returns whether or not a molecule needs to have Hs added to it.
 RDKIT_GRAPHMOL_EXPORT bool needsHs(const ROMol &mol);

-};  // end of namespace MolOps
-};  // end of namespace RDKit
+namespace details {
+//! not recommended for use in other code
+RDKIT_GRAPHMOL_EXPORT void KekulizeFragment(
+    RWMol &mol, const boost::dynamic_bitset<> &atomsToUse,
+    const boost::dynamic_bitset<> &bondsToUse, bool markAtomsBonds = true,
+    unsigned int maxBackTracks = 100);
+}  // namespace details
+
+}  // namespace MolOps
+}  // namespace RDKit

 #endif
diff --git a/Code/GraphMol/SmilesParse/SmilesWrite.cpp b/Code/GraphMol/SmilesParse/SmilesWrite.cpp
index fe8f331a51c..1931584c2ab 100644
--- a/Code/GraphMol/SmilesParse/SmilesWrite.cpp
+++ b/Code/GraphMol/SmilesParse/SmilesWrite.cpp
@@ -294,9 +294,12 @@ std::string FragmentSmilesConstruct(
     const UINT_VECT &ranks, bool doKekule, bool canonical,
     bool doIsomericSmiles, bool allBondsExplicit, bool allHsExplicit,
     bool doRandom, std::vector<unsigned int> &atomOrdering,
+    const boost::dynamic_bitset<> *atomsInPlay = nullptr,
     const boost::dynamic_bitset<> *bondsInPlay = nullptr,
     const std::vector<std::string> *atomSymbols = nullptr,
     const std::vector<std::string> *bondSymbols = nullptr) {
+  PRECONDITION(!atomsInPlay || atomsInPlay->size() >= mol.getNumAtoms(),
+               "bad atomsInPlay");
   PRECONDITION(!bondsInPlay || bondsInPlay->size() >= mol.getNumBonds(),
                "bad bondsInPlay");
   PRECONDITION(!atomSymbols || atomSymbols->size() >= mol.getNumAtoms(),
@@ -304,7 +307,12 @@ std::string FragmentSmilesConstruct(
   PRECONDITION(!bondSymbols || bondSymbols->size() >= mol.getNumBonds(),
                "bad bondSymbols");
   if (doKekule) {
-    MolOps::Kekulize(static_cast<RWMol &>(mol));
+    if (atomsInPlay && bondsInPlay) {
+      MolOps::details::KekulizeFragment(static_cast<RWMol &>(mol), *atomsInPlay,
+                                        *bondsInPlay);
+    } else {
+      MolOps::Kekulize(static_cast<RWMol &>(mol));
+    }
   }

   Canon::MolStack molStack;
@@ -750,8 +758,8 @@ std::string MolFragmentToSmiles(const ROMol &mol,
     bool doRandom = false;
     auto subSmi = SmilesWrite::FragmentSmilesConstruct(
         tmol, nextAtomIdx, colors, ranks, doKekule, canonical, doIsomericSmiles,
-        allBondsExplicit, allHsExplicit, doRandom, atomOrdering, &bondsInPlay,
-        atomSymbols, bondSymbols);
+        allBondsExplicit, allHsExplicit, doRandom, atomOrdering, &atomsInPlay,
+        &bondsInPlay, atomSymbols, bondSymbols);

     res += subSmi;
     colorIt = std::find(colors.begin(), colors.end(), Canon::WHITE_NODE);
diff --git a/Code/GraphMol/catch_graphmol.cpp b/Code/GraphMol/catch_graphmol.cpp
index d7ec1804699..09ee1716e6d 100644
--- a/Code/GraphMol/catch_graphmol.cpp
+++ b/Code/GraphMol/catch_graphmol.cpp
@@ -2046,3 +2046,27 @@ TEST_CASE(
   CHECK_THROWS_AS(conf.setAtomPos(std::numeric_limits<unsigned>::max(), pt),
                   ValueErrorException);
 }
+
+TEST_CASE("KekulizeFragment", "[graphmol]") {
+  SECTION("basics") {
+    auto mol = "CCc1ccccc1"_smiles;
+    REQUIRE(mol);
+    boost::dynamic_bitset<> atomsInPlay(mol->getNumAtoms());
+    for (auto aidx : std::vector<size_t>{0, 1, 2, 3}) {
+      atomsInPlay.set(aidx);
+    }
+    boost::dynamic_bitset<> bondsInPlay(mol->getNumBonds());
+    for (auto bidx : std::vector<size_t>{0, 1, 2}) {
+      bondsInPlay.set(bidx);
+    }
+    MolOps::details::KekulizeFragment(*mol, atomsInPlay, bondsInPlay);
+    CHECK(!mol->getAtomWithIdx(2)->getIsAromatic());
+    CHECK(mol->getAtomWithIdx(4)->getIsAromatic());
+    CHECK(!mol->getBondWithIdx(2)->getIsAromatic());
+    // at the moment that bond still has an aromatic bond order, which isn't
+    // optimal, but that will have to wait until we add a feature to allow
+    // kekulization of conjugated chains.
+    CHECK(mol->getBondWithIdx(2)->getBondType() == Bond::AROMATIC);
+    CHECK(mol->getBondWithIdx(4)->getIsAromatic());
+  }
+}

diff --git a/Code/GraphMol/SmilesParse/catch_tests.cpp b/Code/GraphMol/SmilesParse/catch_tests.cpp
index 67807a0ccdc..821754f58b3 100644
--- a/Code/GraphMol/SmilesParse/catch_tests.cpp
+++ b/Code/GraphMol/SmilesParse/catch_tests.cpp
@@ -855,4 +855,32 @@ TEST_CASE("Github #2788: doKekule=true should kekulize the molecule",
 TEST_CASE("bogus recursive SMARTS", "[smarts]") {
   std::string sma = "C)foo";
   CHECK(SmartsToMol(sma) == nullptr);
-}
\ No newline at end of file
+}
+
+TEST_CASE(
+    "Github #3998 MolFragmentToSmiles failing in Kekulization with "
+    "kekuleSmiles=true") {
+  auto mol = "Cc1ccccc1"_smiles;
+  REQUIRE(mol);
+  SECTION("normal") {
+    std::vector<int> ats{0};
+    std::string smi = MolFragmentToSmiles(*mol, ats);
+    CHECK(smi == "C");
+  }
+  SECTION("kekulized") {
+    std::vector<int> ats{0};
+    bool doIsomericSmiles = true;
+    bool doKekule = true;
+    std::string smi = MolFragmentToSmiles(*mol, ats, nullptr, nullptr, nullptr,
+                                          doIsomericSmiles, doKekule);
+    CHECK(smi == "C");
+  }
+  SECTION("including ring parts") {
+    std::vector<int> ats{0, 1, 2};
+    bool doIsomericSmiles = true;
+    bool doKekule = true;
+    std::string smi = MolFragmentToSmiles(*mol, ats, nullptr, nullptr, nullptr,
+                                          doIsomericSmiles, doKekule);
+    CHECK(smi == "C:CC");
+  }
+}


Fixes #4281 (rdkit/rdkit#4282)

Made the pyridine _N_-oxide pattern more specific and added a test.

The normalization pattern for pyridine N-oxide is not specific enough

Reported by JP Ebejer:

```
from rdkit import Chem
from rdkit.Chem.MolStandardize import rdMolStandardize

m = Chem.MolFromSmiles('Cn1c(=O)c2nc[nH][n+](=O)c2n(C)c1=O')
m
```
![image](https://user-images.githubusercontent.com/5244385/123306106-29642c80-d521-11eb-8ec5-fb897a8b308b.png)
```
rdMolStandardize.Cleanup(m)
RDKit ERROR: [19:20:42] Can't kekulize mol.  Unkekulized atoms: 0 3 4 6 7
```

The molecule is not actually a pyridine _N_-oxide, rather the oxy tautomer of a _N_-hydroxypyridinium ion, but it hits the pyridine _N_-oxide normalization pattern because the latter is not sufficiently specific.

diff --git a/Code/GraphMol/MolStandardize/TransformCatalog/normalizations.in b/Code/GraphMol/MolStandardize/TransformCatalog/normalizations.in
index 6b452219452..8aeb6b44d3e 100644
--- a/Code/GraphMol/MolStandardize/TransformCatalog/normalizations.in
+++ b/Code/GraphMol/MolStandardize/TransformCatalog/normalizations.in
@@ -16,7 +16,7 @@ std::vector<std::pair<std::string, std::string>> defaultNormalizations = {
      "[N,P,As,Sb;X3:1](=[O,S,Se,Te:2])=[O,S,Se,Te:3]>>[*+1:1]([*-1:2])=[*:3]"},
     {"Sulfone to S(=O)(=O)",
      "[S+2:1]([O-:2])([O-:3])>>[S+0:1](=[O-0:2])(=[O-0:3])"},
-    {"Pyridine oxide to n+O-", "[n:1]=[O:2]>>[n+:1][O-:2]"},
+    {"Pyridine oxide to n+O-", "[nH0+0:1]=[OH0+0:2]>>[n+:1][O-:2]"},
     {"Azide to N=N+=N-", "[*:1][N:2]=[N:3]#[N:4]>>[*:1][N:2]=[N+:3]=[N-:4]"},
     {"Diazo/azo to =N+=N-", "[*:1]=[N:2]#[N:3]>>[*:1]=[N+:2]=[N-:3]"},
     {"Sulfoxide to -S+(O-)-",

diff --git a/Code/GraphMol/MolStandardize/testNormalize.cpp b/Code/GraphMol/MolStandardize/testNormalize.cpp
index ae31a9583a3..d65045f43ff 100644
--- a/Code/GraphMol/MolStandardize/testNormalize.cpp
+++ b/Code/GraphMol/MolStandardize/testNormalize.cpp
@@ -501,6 +501,21 @@ void testEmptyMol() {
   TEST_ASSERT(!normalized->getNumAtoms());
 }

+void testGithub4281() {
+  BOOST_LOG(rdInfoLog)
+      << "-----------------------\n Testing Github #3460: "
+         "Normalization rule incorrectly matches [N+]=O rather than n=O"
+      << std::endl;
+  auto mol = "Cn1c(=O)c2nc[nH][n+](=O)c2n(C)c1=O"_smiles;
+  std::stringstream captureLog;
+  rdInfoLog->SetTee(captureLog);
+  Normalizer nn;
+  std::unique_ptr<ROMol> normalized(nn.normalize(*mol));
+  rdInfoLog->ClearTee();
+  auto logged = captureLog.str();
+  TEST_ASSERT(logged.find("FAILED sanitizeMol") == std::string::npos);
+}
+
 int main() {
   RDLog::InitLogs();
 #if 1
@@ -511,5 +526,6 @@ int main() {
   testNormalizeMultipleAltSmarts();
   testGithub3460();
   testEmptyMol();
+  testGithub4281();
   return 0;
 }


Fixes #4346 : trans bonds are now properly honored in macrocycles (rdkit/rdkit#4355)

We didn't have code which handled the possibility that there'd be a trans bond in a ring. :-(

Specified trans stereo being ignored during conformation generation in macrocycles

**Describe the bug**
Here's the example:
```
smis = r''smis = r'''C1CC=CCCCCCCCC1
C1C/C=C/CCCCCCCC1
C1C\C=C/CCCCCCCC1'''.split('\n')
mols = [Chem.MolFromSmiles(x) for x in smis]

mol_hs = [Chem.AddHs(m) for m in mols]
for mol in mol_hs:
    ok = Chem.MolToInchiKey(mol)
    rdDistGeom.EmbedMolecule(mol,randomSeed=0xf00d,enforceChirality=True,useBasicKnowledge=True)
    mol = Chem.RemoveHs(mol)
    Chem.AssignStereochemistryFrom3D(mol,replaceExistingTags=True)
    print(Chem.MolToSmiles(mol),ok, Chem.MolToInchiKey(mol))
```
Output:
```
C1=C/CCCCCCCCCC/1 HYPABJGVBDSCIT-UHFFFAOYSA-N HYPABJGVBDSCIT-OWOJBTEDSA-N
C1=C\CCCCCCCCCC/1 HYPABJGVBDSCIT-OWOJBTEDSA-N HYPABJGVBDSCIT-UPHRSURJSA-N
C1=C\CCCCCCCCCC/1 HYPABJGVBDSCIT-UPHRSURJSA-N HYPABJGVBDSCIT-UPHRSURJSA-N
```
If we open up the macrocycle things are fine:
```
smis = r'''CCC=CCCCCCCCC
CC/C=C/CCCCCCCC
CC\C=C/CCCCCCCC'''.split('\n')
mols = [Chem.MolFromSmiles(x) for x in smis]

mol_hs = [Chem.AddHs(m) for m in mols]
for mol in mol_hs:
    ok = Chem.MolToInchiKey(mol)
    rdDistGeom.EmbedMolecule(mol,randomSeed=0xf00d,enforceChirality=True,useBasicKnowledge=True)
    mol = Chem.RemoveHs(mol)
    Chem.AssignStereochemistryFrom3D(mol,replaceExistingTags=True)
    print(Chem.MolToSmiles(mol),ok, Chem.MolToInchiKey(mol))
```
Output:
```
CC/C=C\CCCCCCCC WLTSXAIICPDFKI-UHFFFAOYSA-N WLTSXAIICPDFKI-ALCCZGGFSA-N
CC/C=C/CCCCCCCC WLTSXAIICPDFKI-FNORWQNLSA-N WLTSXAIICPDFKI-FNORWQNLSA-N
CC/C=C\CCCCCCCC WLTSXAIICPDFKI-ALCCZGGFSA-N WLTSXAIICPDFKI-ALCCZGGFSA-N
```

**Configuration (please complete the following information):**
 - RDKit version: master and previous releases
 - OS: all

diff --git a/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp b/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp
index e37eb79acb1..0d581f48cde 100644
--- a/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp
+++ b/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2004-2019 Greg Landrum and Rational Discovery LLC
+//  Copyright (C) 2004-2021 Greg Landrum and other RDKit contributors
 //
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.
@@ -610,6 +610,7 @@ void _setInRing14Bounds(const ROMol &mol, const Bond *bnd1, const Bond *bnd2,
   path14.bid3 = bid3;
   Bond::BondStereo stype = _getAtomStereo(bnd2, aid1, aid4);
   bool preferCis = false;
+  bool preferTrans = false;

   // we add a check for the ring size here because there's no reason to assume
   // cis bonds in bigger rings.
@@ -634,9 +635,12 @@ void _setInRing14Bounds(const ROMol &mol, const Bond *bnd1, const Bond *bnd2,
     }
   } else if (stype == Bond::STEREOZ || stype == Bond::STEREOCIS) {
     preferCis = true;
+  } else if (stype == Bond::STEREOE || stype == Bond::STEREOTRANS) {
+    preferTrans = true;
   }
-  // std::cerr << "  torsion: " << aid1 << " " << aid4 << ": " << preferCis
-  //           << std::endl;
+  // std::cerr << "  torsion: " << aid1 << " " << aid4 << ": " << preferCis << "
+  // "
+  //           << preferTrans << std::endl;
   if (preferCis) {
     dl = RDGeom::compute14DistCis(bl1, bl2, bl3, ba12, ba23) - GEN_DIST_TOL;
     du = dl + 2 * GEN_DIST_TOL;
@@ -645,6 +649,15 @@ void _setInRing14Bounds(const ROMol &mol, const Bond *bnd1, const Bond *bnd2,
                        bid3] = 1;
     accumData.cisPaths[static_cast<unsigned long>(bid3) * nb * nb + bid2 * nb +
                        bid1] = 1;
+  } else if (preferTrans) {
+    dl = RDGeom::compute14DistTrans(bl1, bl2, bl3, ba12, ba23) - GEN_DIST_TOL;
+    du = dl + 2 * GEN_DIST_TOL;
+    path14.type = Path14Configuration::TRANS;
+    accumData.transPaths[static_cast<unsigned long>(bid1) * nb * nb +
+                         bid2 * nb + bid3] = 1;
+    accumData.transPaths[static_cast<unsigned long>(bid3) * nb * nb +
+                         bid2 * nb + bid1] = 1;
+
   } else {
     // basically we will assume 0 to 180 allowed
     dl = RDGeom::compute14DistCis(bl1, bl2, bl3, ba12, ba23);
@@ -812,7 +825,7 @@ bool _checkNhChChNh(const Atom *atm1, const Atom *atm2, const Atom *atm3,
 //      2   5  <- 2 is an oxygen/nitrogen
 bool _checkAmideEster14(const Bond *bnd1, const Bond *bnd3, const Atom *atm1,
                         const Atom *atm2, const Atom *atm3, const Atom *atm4) {
-  unsigned int a1Num = atm1->getAtomicNum();
+  RDUNUSED_PARAM(atm1);
   unsigned int a2Num = atm2->getAtomicNum();
   unsigned int a3Num = atm3->getAtomicNum();
   unsigned int a4Num = atm4->getAtomicNum();
@@ -917,6 +930,7 @@ bool _isCarbonyl(const ROMol &mol, const Atom *at) {
 bool _checkAmideEster15(const ROMol &mol, const Bond *bnd1, const Bond *bnd3,
                         const Atom *atm1, const Atom *atm2, const Atom *atm3,
                         const Atom *atm4) {
+  RDUNUSED_PARAM(atm1);
   RDUNUSED_PARAM(atm4);
   unsigned int a2Num = atm2->getAtomicNum();
   if ((a2Num == 8) || ((a2Num == 7) && (atm2->getTotalNumHs(true) == 1))) {
diff --git a/Code/GraphMol/DistGeomHelpers/Embedder.cpp b/Code/GraphMol/DistGeomHelpers/Embedder.cpp
index d260d869f85..f99af457931 100644
--- a/Code/GraphMol/DistGeomHelpers/Embedder.cpp
+++ b/Code/GraphMol/DistGeomHelpers/Embedder.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2004-2019 Greg Landrum and Rational Discovery LLC
+//  Copyright (C) 2004-2021 Greg Landrum and other RDKit contributors
 //
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.

diff --git a/Code/GraphMol/DistGeomHelpers/catch_tests.cpp b/Code/GraphMol/DistGeomHelpers/catch_tests.cpp
index 64c98fe38e6..8f8ecfd1087 100644
--- a/Code/GraphMol/DistGeomHelpers/catch_tests.cpp
+++ b/Code/GraphMol/DistGeomHelpers/catch_tests.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2021 Greg Landrum
+//  Copyright (C) 2021 Greg Landrum and other RDKit contributors
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.
 //  The contents are covered by the terms of the BSD license
@@ -172,4 +172,35 @@ TEST_CASE("update parameters from JSON") {
       CHECK(v1.angleTo(v2) == Approx(M_PI / 2).margin(0.15));
     }
   }
+}
+
+TEST_CASE(
+    "github #4346: Specified cis/trans stereo being ignored during "
+    "conformation generation in macrocycles") {
+  SECTION("basics 1") {
+    auto m1 = "C1C/C=C/CCCCCCCC1"_smiles;
+    REQUIRE(m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOE);
+    MolOps::addHs(*m1);
+    DGeomHelpers::EmbedParameters params = DGeomHelpers::KDG;
+    params.randomSeed = 0xf00d;
+    CHECK(DGeomHelpers::EmbedMolecule(*m1, params) != -1);
+    MolOps::assignStereochemistryFrom3D(*m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOE);
+  }
+  SECTION("basics 2") {
+    auto m1 = "C1C/C=C\\CCCCCCCC1"_smiles;
+    REQUIRE(m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOZ);
+    MolOps::addHs(*m1);
+    DGeomHelpers::EmbedParameters params = DGeomHelpers::KDG;
+    params.randomSeed = 0xf00d;
+    CHECK(DGeomHelpers::EmbedMolecule(*m1, params) != -1);
+    MolOps::assignStereochemistryFrom3D(*m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOZ);
+  }
 }
\ No newline at end of file


Fixes #4582 (rdkit/rdkit#4587)

The changes in #4190 introduced a problem in the SMILES for double bond stereo around ring closures in some rare cases (~60 molecules in ChEMBL27). This fixes that problem.

This isn't the most satisfying fix since there are definitely still some errors that can be found when using random SMILES output order, but I'm not sure how long it's going to take to fix the full problem, so I'm suggesting we merge this under the theory that a partial fix is better than nothing.

As an aside: the code in `Canon.cpp::canonicalizeDoubleBond()` really ought to be refactored and cleaned up.

Incorrect double bond stereo in output SMILES around ring closures

**Describe the bug**

Code to reproduce this:
```
from rdkit import Chem
m = Chem.MolFromMolBlock('''CHEMBL409450
     RDKit          2D

 22 25  0  0  0  0  0  0  0  0999 V2000
   -1.1669    1.3591    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.6820    0.6916    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -1.9516    1.1041    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -1.9516    0.2791    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
   -2.5647    1.6562    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -2.3931    2.4631    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -1.6085    2.7181    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.9954    2.1661    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -1.1669    0.0242    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.9120   -0.7604    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -1.3969   -1.4279    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.9120   -2.0953    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
   -0.1274   -1.8404    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.1274   -1.0154    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    0.5871   -2.2529    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    1.3016   -1.8404    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    1.3016   -1.0154    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    0.5871   -0.6029    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -2.2219   -1.4279    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
    0.1430    0.6916    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
    0.5555    1.4061    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
    2.0160   -2.2529    0.0000 Br  0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0
  1  3  2  0
  1  8  1  0
  9  2  1  0
  2 20  2  0
  3  4  1  0
  3  5  1  0
  9  4  1  0
  5  6  2  0
  6  7  1  0
  8  7  2  0
  9 10  2  0
 10 11  1  0
 10 14  1  0
 11 12  1  0
 11 19  2  0
 13 12  1  0
 13 14  2  0
 13 15  1  0
 14 18  1  0
 15 16  2  0
 16 17  1  0
 22 16  1  0
 17 18  2  0
 20 21  1  0
M  END''')
print(Chem.MolToSmiles(m))
```

The output is `O=C1Nc2cc(Br)ccc2/C1=C1/Nc2ccccc2/C1=N/O`, but it should be  `O=C1Nc2cc(Br)ccc2/C1=C1/Nc2ccccc2/C1=N\O`

This was introduced as part of #4190

**Configuration (please complete the following information):**
 - RDKit version: >=2021.03.3
 - OS: all

diff --git a/.azure-pipelines/linux_build_cartridge.yml b/.azure-pipelines/linux_build_cartridge.yml
index 500cd038a17..e0a54564db5 100644
--- a/.azure-pipelines/linux_build_cartridge.yml
+++ b/.azure-pipelines/linux_build_cartridge.yml
@@ -1,10 +1,12 @@
 steps:
 - bash: |
     sudo apt-get update
-    sudo apt-get remove postgresql postgresql-13 postgresql-client-13 postgresql-common postgresql-client-common
-    sudo apt purge postgresql postgresql-13 postgresql-client-13 postgresql-common postgresql-client-common
+    sudo apt-get remove postgresql-14 postgresql-client-14
+    sudo apt-get remove postgresql postgresql-common postgresql-client-common
+    sudo apt purge postgresql-14 postgresql-client-14
+    sudo apt purge postgresql postgresql-common postgresql-client-common
     sudo apt-get install -y libboost-all-dev postgresql-common postgresql-12 postgresql-server-dev-12
-    echo "add user"
+    echo "add postgres user"
     sudo -u postgres createuser -s `whoami`
   displayName: Setup build environment
 - bash: |
diff --git a/Code/GraphMol/Canon.cpp b/Code/GraphMol/Canon.cpp
index faa822a07df..ebd50d68781 100644
--- a/Code/GraphMol/Canon.cpp
+++ b/Code/GraphMol/Canon.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2001-2020 Greg Landrum and Rational Discovery LLC
+//  Copyright (C) 2001-2021 Greg Landrum and other RDKit contributors
 //
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.
@@ -136,6 +136,17 @@ void switchBondDir(Bond *bond) {
   }
 }

+namespace {
+bool isClosingRingBond(Bond *bond) {
+  if (bond == nullptr) {
+    return false;
+  }
+  auto beginIdx = bond->getBeginAtomIdx();
+  auto endIdx = bond->getEndAtomIdx();
+  return beginIdx > endIdx && beginIdx - endIdx > 1 &&
+         bond->hasProp(common_properties::_TraversalRingClosureBond);
+}
+}  // namespace
 // FIX: this may only be of interest from the SmilesWriter, should we
 // move it there?
 //
@@ -354,27 +365,17 @@ void canonicalizeDoubleBond(Bond *dblBond, UINT_VECT &bondVisitOrders,
     // stereochemistry, we need to flip the setting on the other bond:
     const INT_VECT &stereoAtoms = dblBond->getStereoAtoms();

-    auto isClosingRingBond = [](Bond *bond) {
-      if (bond == nullptr) {
-        return false;
-      }
-      auto beginIdx = bond->getBeginAtomIdx();
-      auto endIdx = bond->getEndAtomIdx();
-      return beginIdx > endIdx && beginIdx - endIdx > 1 &&
-             bond->hasProp(common_properties::_TraversalRingClosureBond);
-    };
-
     auto isFlipped = false;

-    if (atom1->getDegree() == 3 &&
+    if (atom1->getDegree() == 3 &&  // atom1ControllingBond == firstFromAtom1 &&
         std::find(stereoAtoms.begin(), stereoAtoms.end(),
                   static_cast<int>(atom1ControllingBond->getOtherAtomIdx(
                       atom1->getIdx()))) == stereoAtoms.end()) {
       isFlipped = true;
       atom2Dir = flipBondDir(atom2Dir);
     }
-    // std::cerr<<" 0 set bond 2: "<<firstFromAtom2->getIdx()<<"
-    // "<<atom2Dir<<std::endl;
+    // std::cerr << " 0 set bond 2: " << firstFromAtom2->getIdx() << " "
+    //           << atom2Dir << std::endl;
     if (atom2->getDegree() == 3 &&
         std::find(stereoAtoms.begin(), stereoAtoms.end(),
                   static_cast<int>(firstFromAtom2->getOtherAtomIdx(
@@ -383,14 +384,12 @@ void canonicalizeDoubleBond(Bond *dblBond, UINT_VECT &bondVisitOrders,
       atom2Dir = flipBondDir(atom2Dir);
     }

-    if (!isFlipped && (isClosingRingBond(dblBond) ||
-                       (isClosingRingBond(secondFromAtom1) &&
-                        !secondFromAtom1->getIsAromatic() &&
-                        secondFromAtom1->getBondDir() != Bond::NONE))) {
+    if (!isFlipped && isClosingRingBond(dblBond)) {
       atom2Dir = flipBondDir(atom2Dir);
     }
-    // std::cerr<<" 1 set bond 2: "<<firstFromAtom2->getIdx()<<"
-    // "<<atom2Dir<<std::endl;
+
+    // std::cerr << " 1 set bond 2: " << firstFromAtom2->getIdx() << " "
+    //           << atom2Dir << std::endl;
     firstFromAtom2->setBondDir(atom2Dir);

     bondDirCounts[firstFromAtom2->getIdx()] += 1;
@@ -554,7 +553,11 @@ void canonicalizeDoubleBond(Bond *dblBond, UINT_VECT &bondVisitOrders,
     if (dblBondPresent && otherAtom3Bond &&
         otherAtom3Bond->getBondDir() == Bond::NONE) {
       // std::cerr<<"set!"<<std::endl;
-      otherAtom3Bond->setBondDir(firstFromAtom2->getBondDir());
+      auto dir = firstFromAtom2->getBondDir();
+      if (isClosingRingBond(otherAtom3Bond)) {
+        dir = flipBondDir(dir);
+      }
+      otherAtom3Bond->setBondDir(dir);
       bondDirCounts[otherAtom3Bond->getIdx()] += 1;
       atomDirCounts[atom3->getIdx()] += 1;
     }


Fixes #4878: cannot parse coordinate bonds from CXSMARTS (rdkit/rdkit#4883)

this one slipped through the cracks when adding support for coordinate bonds to the SMILES parser

**Describe the bug**
Here's the reproducible:
```
In [14]: m = Chem.MolFromSmarts('[Fe]OC |C:1.0|')
[16:59:13] BOND NOT FOUND! 0 involving atom 1
```
demonstration that it works with SMILES:
```
In [15]: m = Chem.MolFromSmiles('[Fe]OC |C:1.0|')
```
The problem is that the modifications to track SMILES bond index were not added to the SMARTS parser.

**Configuration (please complete the following information):**
 - RDKit version: current release and master
 - OS: all

diff --git a/Code/GraphMol/SmilesParse/SmilesParse.cpp b/Code/GraphMol/SmilesParse/SmilesParse.cpp
index 1379852b468..a5a5da008e6 100644
--- a/Code/GraphMol/SmilesParse/SmilesParse.cpp
+++ b/Code/GraphMol/SmilesParse/SmilesParse.cpp
@@ -64,8 +64,10 @@ int smarts_parse_helper(const std::string &inp,
   TEST_ASSERT(!yysmarts_lex_init(&scanner));
   try {
     size_t ltrim = setup_smarts_string(inp, scanner);
-    res = yysmarts_parse(inp.c_str() + ltrim, &molVect, atom, bond, scanner,
-                         start_tok);
+    unsigned numAtomsParsed = 0;
+    unsigned numBondsParsed = 0;
+    res = yysmarts_parse(inp.c_str() + ltrim, &molVect, atom, bond,
+                         numAtomsParsed, numBondsParsed, scanner, start_tok);
   } catch (...) {
     yysmarts_lex_destroy(scanner);
     throw;
diff --git a/Code/GraphMol/SmilesParse/smarts.tab.cpp.cmake b/Code/GraphMol/SmilesParse/smarts.tab.cpp.cmake
index 6d00ca0c998..5062d7e1778 100644
--- a/Code/GraphMol/SmilesParse/smarts.tab.cpp.cmake
+++ b/Code/GraphMol/SmilesParse/smarts.tab.cpp.cmake
@@ -74,9 +74,8 @@
 #line 1 "smarts.yy"

-  // $Id$
   //
-  //  Copyright (C) 2003-2018 Greg Landrum and Rational Discovery LLC
+  //  Copyright (C) 2003-2022 Greg Landrum and other RDKit contributors
   //
   //   @@ All Rights Reserved  @@
   //
@@ -110,20 +109,16 @@ namespace {
 void
 yysmarts_error( const char *input,
                 std::vector<RDKit::RWMol *> *ms,
-                RDKit::Atom* &lastAtom,
-                RDKit::Bond* &lastBond,
-		void *scanner,int start_token, const char * msg )
+                RDKit::Atom* &,
+                RDKit::Bond* &,
+                unsigned int &,unsigned int &,
+		void *,int , const char *msg  )
 {
-  RDUNUSED_PARAM(input);
-  RDUNUSED_PARAM(lastAtom);
-  RDUNUSED_PARAM(lastBond);
-  RDUNUSED_PARAM(scanner);
-  RDUNUSED_PARAM(start_token);
   yyErrorCleanup(ms);
   BOOST_LOG(rdErrorLog) << "SMARTS Parse Error: " << msg << " while parsing: " << input << std::endl;
 }

-#line 127 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 122 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"

 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -221,14 +216,14 @@ extern int yysmarts_debug;
 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
 union YYSTYPE
 {
-#line 68 "smarts.yy"
+#line 65 "smarts.yy"

   int                      moli;
   RDKit::QueryAtom * atom;
   RDKit::QueryBond * bond;
   int                      ival;

-#line 232 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 227 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"

 };
 typedef union YYSTYPE YYSTYPE;
@@ -238,14 +233,14 @@ typedef union YYSTYPE YYSTYPE;

-int yysmarts_parse (const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token);
+int yysmarts_parse (const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token);
 /* "%code provides" blocks.  */
-#line 63 "smarts.yy"
+#line 60 "smarts.yy"

 #define YY_DECL int yylex \
                (YYSTYPE * yylval_param , yyscan_t yyscanner, int& start_token)

-#line 249 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 244 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"

 #endif /* !YY_YYSMARTS_SCRATCH_RDKIT_GIT_CODE_GRAPHMOL_SMILESPARSE_SMARTS_TAB_HPP_INCLUDED  */

@@ -614,18 +609,18 @@ static const yytype_int8 yytranslate[] =
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_int16 yyrline[] =
 {
-       0,   112,   112,   115,   119,   122,   125,   129,   133,   136,
-     141,   144,   152,   153,   154,   155,   163,   171,   196,   216,
-     221,   252,   272,   285,   286,   297,   298,   299,   303,   326,
-     330,   335,   341,   350,   356,   364,   372,   385,   391,   398,
-     404,   427,   430,   436,   437,   441,   458,   482,   483,   488,
-     489,   494,   495,   500,   501,   502,   503,   504,   505,   506,
-     509,   512,   515,   518,   521,   524,   530,   536,   543,   551,
-     559,   565,   571,   577,   583,   589,   590,   597,   598,   601,
-     604,   607,   610,   613,   618,   626,   638,   643,   648,   652,
-     656,   660,   663,   664,   671,   672,   678,   684,   690,   695,
-     702,   703,   704,   705,   706,   707,   711,   712,   713,   714,
-     715,   716,   717,   722,   723,   727,   728,   737,   738
+       0,   109,   109,   112,   116,   119,   122,   126,   130,   133,
+     138,   141,   149,   150,   151,   152,   160,   168,   194,   215,
+     220,   254,   275,   294,   295,   306,   307,   308,   312,   335,
+     339,   344,   350,   359,   365,   373,   381,   394,   400,   407,
+     413,   436,   439,   445,   446,   450,   467,   491,   492,   497,
+     498,   503,   504,   509,   510,   511,   512,   513,   514,   515,
+     518,   521,   524,   527,   530,   533,   539,   545,   552,   560,
+     568,   574,   580,   586,   592,   598,   599,   606,   607,   610,
+     613,   616,   619,   622,   627,   635,   647,   652,   657,   661,
+     665,   669,   672,   673,   680,   681,   687,   693,   699,   704,
+     711,   712,   713,   714,   715,   716,   720,   721,   722,   723,
+     724,   725,   726,   731,   732,   736,   737,   746,   747
 };
 #endif

@@ -965,7 +960,7 @@ static const yytype_int8 yyr2[] =
       }                                                           \
     else                                                          \
       {                                                           \
-        yyerror (input, molList, lastAtom, lastBond, scanner, start_token, YY_("syntax error: cannot back up")); \
+        yyerror (input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token, YY_("syntax error: cannot back up")); \
         YYERROR;                                                  \
       }                                                           \
   while (0)
@@ -1002,7 +997,7 @@ do {                                                                      \
     {                                                                     \
       YYFPRINTF (stderr, "%s ", Title);                                   \
       yy_symbol_print (stderr,                                            \
-                  Type, Value, input, molList, lastAtom, lastBond, scanner, start_token); \
+                  Type, Value, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token); \
       YYFPRINTF (stderr, "\n");                                           \
     }                                                                     \
 } while (0)
@@ -1013,7 +1008,7 @@ do {                                                                      \
 `-----------------------------------*/

 static void
-yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token)
+yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token)
 {
   FILE *yyoutput = yyo;
   YYUSE (yyoutput);
@@ -1021,6 +1016,8 @@ yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, co
   YYUSE (molList);
   YYUSE (lastAtom);
   YYUSE (lastBond);
+  YYUSE (numAtomsParsed);
+  YYUSE (numBondsParsed);
   YYUSE (scanner);
   YYUSE (start_token);
   if (!yyvaluep)
@@ -1040,12 +1037,12 @@ yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, co
 `---------------------------*/

 static void
-yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token)
+yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token)
 {
   YYFPRINTF (yyo, "%s %s (",
              yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);

-  yy_symbol_value_print (yyo, yytype, yyvaluep, input, molList, lastAtom, lastBond, scanner, start_token);
+  yy_symbol_value_print (yyo, yytype, yyvaluep, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token);
   YYFPRINTF (yyo, ")");
 }

@@ -1078,7 +1075,7 @@ do {                                                            \
 `------------------------------------------------*/

 static void
-yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token)
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token)
 {
   int yylno = yyrline[yyrule];
   int yynrhs = yyr2[yyrule];
@@ -1092,7 +1089,7 @@ yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, const char *inpu
       yy_symbol_print (stderr,
                        yystos[+yyssp[yyi + 1 - yynrhs]],
                        &yyvsp[(yyi + 1) - (yynrhs)]
-                                              , input, molList, lastAtom, lastBond, scanner, start_token);
+                                              , input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token);
       YYFPRINTF (stderr, "\n");
     }
 }
@@ -1100,7 +1097,7 @@ yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, const char *inpu
 # define YY_REDUCE_PRINT(Rule)          \
 do {                                    \
   if (yydebug)                          \
-    yy_reduce_print (yyssp, yyvsp, Rule, input, molList, lastAtom, lastBond, scanner, start_token); \
+    yy_reduce_print (yyssp, yyvsp, Rule, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token); \
 } while (0)

 /* Nonzero means print parse trace.  It is left uninitialized so that
@@ -1368,13 +1365,15 @@ yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg,
 `-----------------------------------------------*/

 static void
-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token)
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token)
 {
   YYUSE (yyvaluep);
   YYUSE (input);
   YYUSE (molList);
   YYUSE (lastAtom);
   YYUSE (lastBond);
+  YYUSE (numAtomsParsed);
+  YYUSE (numBondsParsed);
   YYUSE (scanner);
   YYUSE (start_token);
   if (!yymsg)
@@ -1385,135 +1384,135 @@ yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, const char *input,
   switch (yytype)
     {
     case 8: /* ATOM_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1391 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1390 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 9: /* SIMPLE_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1397 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1396 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 10: /* COMPLEX_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1403 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1402 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 11: /* RINGSIZE_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1409 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1408 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 12: /* RINGBOND_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1415 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1414 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 13: /* IMPLICIT_H_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1421 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1420 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 14: /* HYB_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1427 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1426 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 15: /* HETERONEIGHBOR_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1433 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1432 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 16: /* ALIPHATIC  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1439 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1438 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 17: /* ALIPHATICHETERONEIGHBOR_ATOM_QUERY_TOKEN  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1445 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1444 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 44: /* BOND_TOKEN  */
-#line 104 "smarts.yy"
+#line 101 "smarts.yy"
             { delete ((*yyvaluep).bond); }
-#line 1451 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1450 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 51: /* atomd  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1457 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1456 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 52: /* hydrogen_atom  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1463 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1462 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 53: /* atom_expr  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1469 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1468 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 54: /* point_query  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1475 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1474 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 55: /* recursive_query  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1481 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1480 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 56: /* atom_query  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1487 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1486 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 57: /* possible_range_query  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1493 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1492 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 58: /* simple_atom  */
-#line 103 "smarts.yy"
+#line 100 "smarts.yy"
             { delete ((*yyvaluep).atom); }
-#line 1499 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1498 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 59: /* bond_expr  */
-#line 104 "smarts.yy"
+#line 101 "smarts.yy"
             { delete ((*yyvaluep).bond); }
-#line 1505 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1504 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 60: /* bond_query  */
-#line 104 "smarts.yy"
+#line 101 "smarts.yy"
             { delete ((*yyvaluep).bond); }
-#line 1511 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1510 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

     case 61: /* bondd  */
-#line 104 "smarts.yy"
+#line 101 "smarts.yy"
             { delete ((*yyvaluep).bond); }
-#line 1517 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1516 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
         break;

       default:
@@ -1530,7 +1529,7 @@ yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, const char *input,
 `----------*/

 int
-yyparse (const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token)
+yyparse (const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token)
 {
 /* The lookahead symbol.  */
 int yychar;
@@ -1785,103 +1784,103 @@ yyreduce:
   switch (yyn)
     {
   case 2:
-#line 112 "smarts.yy"
+#line 109 "smarts.yy"
               {
 // the molList has already been updated, no need to do anything
 }
-#line 1793 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1792 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 3:
-#line 115 "smarts.yy"
+#line 112 "smarts.yy"
                              {
   lastAtom = (yyvsp[-1].atom);
   YYACCEPT;
 }
-#line 1802 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1801 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 4:
-#line 119 "smarts.yy"
+#line 116 "smarts.yy"
                           {
   YYABORT;
 }
-#line 1810 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1809 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 5:
-#line 122 "smarts.yy"
+#line 119 "smarts.yy"
              {
   YYABORT;
 }
-#line 1818 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1817 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 6:
-#line 125 "smarts.yy"
+#line 122 "smarts.yy"
                                  {
   lastBond = (yyvsp[-1].bond);
   YYACCEPT;
 }
-#line 1827 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1826 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 7:
-#line 129 "smarts.yy"
+#line 126 "smarts.yy"
                        {
   delete (yyvsp[0].bond);
   YYABORT;
 }
-#line 1836 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1835 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 8:
-#line 133 "smarts.yy"
+#line 130 "smarts.yy"
              {
   YYABORT;
 }
-#line 1844 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1843 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 9:
-#line 136 "smarts.yy"
+#line 133 "smarts.yy"
                             {
   yyerrok;
   yyErrorCleanup(molList);
   YYABORT;
 }
-#line 1854 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1853 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 10:
-#line 141 "smarts.yy"
+#line 138 "smarts.yy"
                        {
   YYACCEPT;
 }
-#line 1862 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1861 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 11:
-#line 144 "smarts.yy"
+#line 141 "smarts.yy"
                   {
   yyerrok;
   yyErrorCleanup(molList);
   YYABORT;
 }
-#line 1872 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1871 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 15:
-#line 155 "smarts.yy"
+#line 152 "smarts.yy"
             {
   delete (yyvsp[0].atom);
   YYABORT;
 }
-#line 1881 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1880 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 16:
-#line 163 "smarts.yy"
+#line 160 "smarts.yy"
            {
   int sz     = molList->size();
   molList->resize( sz + 1);
@@ -1890,11 +1889,11 @@ yyreduce:
   //delete $1;
   (yyval.moli) = sz;
 }
-#line 1894 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1893 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 17:
-#line 171 "smarts.yy"
+#line 168 "smarts.yy"
                   {
   RWMol *mp = (*molList)[(yyval.moli)];
   Atom *a1 = mp->getActiveAtom();
@@ -1915,6 +1914,7 @@ yyreduce:
   newB->setOwningMol(mp);
   newB->setBeginAtomIdx(atomIdx1);
   newB->setEndAtomIdx(atomIdx2);
+  newB->setProp("_cxsmilesBondIdx",numBondsParsed++);
   mp->addBond(newB);
   delete newB;
   //delete $2;
@@ -1923,7 +1923,7 @@ yyreduce:
     break;

   case 18:
-#line 196 "smarts.yy"
+#line 194 "smarts.yy"
                        {
   RWMol *mp = (*molList)[(yyval.moli)];
   int atomIdx1 = mp->getActiveAtom()->getIdx();
@@ -1940,23 +1940,24 @@ yyreduce:
     (yyvsp[-1].bond)->setBeginAtomIdx(atomIdx1);
     (yyvsp[-1].bond)->setEndAtomIdx(atomIdx2);
   }
+  (yyvsp[-1].bond)->setProp("_cxsmilesBondIdx",numBondsParsed++);
   mp->addBond((yyvsp[-1].bond));
   delete (yyvsp[-1].bond);
 }
-#line 1947 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1948 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 19:
-#line 216 "smarts.yy"
+#line 215 "smarts.yy"
                             {
   RWMol *mp = (*molList)[(yyval.moli)];
   mp->addAtom((yyvsp[0].atom),true,true);
 }
-#line 1956 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1957 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 20:
-#line 221 "smarts.yy"
+#line 220 "smarts.yy"
                   {
   RWMol * mp = (*molList)[(yyval.moli)];
   Atom *atom=mp->getActiveAtom();
@@ -1975,6 +1976,9 @@ yyreduce:
   newB->setOwningMol(mp);
   newB->setBeginAtomIdx(atom->getIdx());
   mp->setBondBookmark(newB,(yyvsp[0].ival));
+  if(!(mp->getAllBondsWithBookmark((yyvsp[0].ival)).size()%2)){
+    newB->setProp("_cxsmilesBondIdx",numBondsParsed++);
+  }
   mp->setAtomBookmark(atom,(yyvsp[0].ival));

   SmilesParseOps::CheckRingClosureBranchStatus(atom,mp);
@@ -1987,11 +1991,11 @@ yyreduce:
   atom->setProp(RDKit::common_properties::_RingClosures,tmp);

 }
-#line 1991 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 1995 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 21:
-#line 252 "smarts.yy"
+#line 254 "smarts.yy"
                             {
   RWMol * mp = (*molList)[(yyval.moli)];
   Atom *atom=mp->getActiveAtom();
@@ -1999,6 +2003,7 @@ yyreduce:
   mp->setBondBookmark((yyvsp[-1].bond),(yyvsp[0].ival));
   (yyvsp[-1].bond)->setOwningMol(mp);
   (yyvsp[-1].bond)->setBeginAtomIdx(atom->getIdx());
+  (yyvsp[-1].bond)->setProp("_cxsmilesBondIdx",numBondsParsed++);
   mp->setAtomBookmark(atom,(yyvsp[0].ival));

   SmilesParseOps::CheckRingClosureBranchStatus(atom,mp);
@@ -2011,32 +2016,38 @@ yyreduce:
   atom->setProp(RDKit::common_properties::_RingClosures,tmp);

 }
-#line 2015 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2020 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 22:
-#line 272 "smarts.yy"
+#line 275 "smarts.yy"
              {
   RWMol *m1_p = (*molList)[(yyval.moli)],*m2_p=(*molList)[(yyvsp[0].moli)];
+  unsigned int origNumAts = m1_p->getNumAtoms();
+  Atom *active = m1_p->getActiveAtom();
   // FIX: handle generic bonds here
   SmilesParseOps::AddFragToMol(m1_p,m2_p,Bond::UNSPECIFIED,Bond::NONE);
   delete m2_p;
+  Bond *bond = m1_p->getBondBetweenAtoms(active->getIdx(),origNumAts);
+  if(bond){
+    bond->setProp("_cxsmilesBondIdx",numBondsParsed++);
+  }
   int sz = molList->size();
   if ( sz==(yyvsp[0].moli)+1) {
     molList->resize( sz-1 );
   }
 }
-#line 2030 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2041 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 23:
-#line 285 "smarts.yy"
+#line 294 "smarts.yy"
                                                { (yyval.moli) = (yyvsp[-1].moli); }
-#line 2036 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2047 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 24:
-#line 286 "smarts.yy"
+#line 295 "smarts.yy"
                                                    {
   // FIX: this needs to handle arbitrary bond_exprs
   (yyval.moli) = (yyvsp[-1].moli);
@@ -2045,56 +2056,56 @@ yyreduce:
   (yyvsp[-2].bond)->setBeginAtomIdx(0);
   (*molList)[ sz-1 ]->setBondBookmark((yyvsp[-2].bond),ci_LEADING_BOND);
 }
-#line 2049 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2060 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 27:
-#line 300 "smarts.yy"
+#line 309 "smarts.yy"
 {
   (yyval.atom) = (yyvsp[-1].atom);
 }
-#line 2057 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2068 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 28:
-#line 304 "smarts.yy"
+#line 313 "smarts.yy"
 {
   (yyval.atom) = (yyvsp[-3].atom);
   (yyval.atom)->setProp(RDKit::common_properties::molAtomMapNumber,(yyvsp[-1].ival));
 }
-#line 2066 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2077 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 29:
-#line 327 "smarts.yy"
+#line 336 "smarts.yy"
 {
   (yyval.atom) = new QueryAtom(1);
 }
-#line 2074 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2085 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 30:
-#line 331 "smarts.yy"
+#line 340 "smarts.yy"
 {
   (yyval.atom) = new QueryAtom(1);
   (yyval.atom)->setProp(RDKit::common_properties::molAtomMapNumber,(yyvsp[-1].ival));
 }
-#line 2083 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2094 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 31:
-#line 335 "smarts.yy"
+#line 344 "smarts.yy"
                                                   {
   QueryAtom *newQ = new QueryAtom(1);
   newQ->setIsotope((yyvsp[-2].ival));
   newQ->expandQuery(makeAtomIsotopeQuery((yyvsp[-2].ival)),Queries::COMPOSITE_AND,true);
   (yyval.atom)=newQ;
 }
-#line 2094 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2105 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 32:
-#line 341 "smarts.yy"
+#line 350 "smarts.yy"
                                                                      {
   QueryAtom *newQ = new QueryAtom(1);
   newQ->setIsotope((yyvsp[-4].ival));
@@ -2103,22 +2114,22 @@ yyreduce:

   (yyval.atom)=newQ;
 }
-#line 2107 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2118 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 33:
-#line 350 "smarts.yy"
+#line 359 "smarts.yy"
                                                        {
   QueryAtom *newQ = new QueryAtom(1);
   newQ->setFormalCharge((yyvsp[-1].ival));
   newQ->expandQuery(makeAtomFormalChargeQuery((yyvsp[-1].ival)),Queries::COMPOSITE_AND,true);
   (yyval.atom)=newQ;
 }
-#line 2118 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2129 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 34:
-#line 356 "smarts.yy"
+#line 365 "smarts.yy"
                                                                           {
   QueryAtom *newQ = new QueryAtom(1);
   newQ->setFormalCharge((yyvsp[-3].ival));
@@ -2127,11 +2138,11 @@ yyreduce:

   (yyval.atom)=newQ;
 }
-#line 2131 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2142 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 35:
-#line 364 "smarts.yy"
+#line 373 "smarts.yy"
                                                               {
   QueryAtom *newQ = new QueryAtom(1);
   newQ->setIsotope((yyvsp[-3].ival));
@@ -2140,11 +2151,11 @@ yyreduce:
   newQ->expandQuery(makeAtomFormalChargeQuery((yyvsp[-1].ival)),Queries::COMPOSITE_AND,true);
   (yyval.atom)=newQ;
 }
-#line 2144 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2155 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 36:
-#line 372 "smarts.yy"
+#line 381 "smarts.yy"
                                                                                {
   QueryAtom *newQ = new QueryAtom(1);
   newQ->setIsotope((yyvsp[-5].ival));
@@ -2155,22 +2166,22 @@ yyreduce:

   (yyval.atom)=newQ;
 }
-#line 2159 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2170 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 37:
-#line 385 "smarts.yy"
+#line 394 "smarts.yy"
                                          {
   (yyvsp[-2].atom)->expandQuery((yyvsp[0].atom)->getQuery()->copy(),Queries::COMPOSITE_AND,true);
   if((yyvsp[-2].atom)->getChiralTag()==Atom::CHI_UNSPECIFIED) (yyvsp[-2].atom)->setChiralTag((yyvsp[0].atom)->getChiralTag());
   SmilesParseOps::ClearAtomChemicalProps((yyvsp[-2].atom));
   delete (yyvsp[0].atom);
 }
-#line 2170 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2181 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 38:
-#line 391 "smarts.yy"
+#line 400 "smarts.yy"
                                {
   (yyvsp[-2].atom)->expandQuery((yyvsp[0].atom)->getQuery()->copy(),Queries::COMPOSITE_OR,true);
   if((yyvsp[-2].atom)->getChiralTag()==Atom::CHI_UNSPECIFIED) (yyvsp[-2].atom)->setChiralTag((yyvsp[0].atom)->getChiralTag());
@@ -2178,22 +2189,22 @@ yyreduce:
   (yyvsp[-2].atom)->setAtomicNum(0);
   delete (yyvsp[0].atom);
 }
-#line 2182 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2193 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 39:
-#line 398 "smarts.yy"
+#line 407 "smarts.yy"
                                  {
   (yyvsp[-2].atom)->expandQuery((yyvsp[0].atom)->getQuery()->copy(),Queries::COMPOSITE_AND,true);
   if((yyvsp[-2].atom)->getChiralTag()==Atom::CHI_UNSPECIFIED) (yyvsp[-2].atom)->setChiralTag((yyvsp[0].atom)->getChiralTag());
   SmilesParseOps::ClearAtomChemicalProps((yyvsp[-2].atom));
   delete (yyvsp[0].atom);
 }
-#line 2193 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2204 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 40:
-#line 404 "smarts.yy"
+#line 413 "smarts.yy"
                         {
   (yyvsp[-1].atom)->expandQuery((yyvsp[0].atom)->getQuery()->copy(),Queries::COMPOSITE_AND,true);
   if((yyvsp[-1].atom)->getChiralTag()==Atom::CHI_UNSPECIFIED) (yyvsp[-1].atom)->setChiralTag((yyvsp[0].atom)->getChiralTag());
@@ -2217,22 +2228,22 @@ yyreduce:
   }
   delete (yyvsp[0].atom);
 }
-#line 2221 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2232 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 42:
-#line 430 "smarts.yy"
+#line 439 "smarts.yy"
                                    {
   (yyvsp[0].atom)->getQuery()->setNegation(!((yyvsp[0].atom)->getQuery()->getNegation()));
   (yyvsp[0].atom)->setAtomicNum(0);
   SmilesParseOps::ClearAtomChemicalProps((yyvsp[0].atom));
   (yyval.atom) = (yyvsp[0].atom);
 }
-#line 2232 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2243 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 45:
-#line 441 "smarts.yy"
+#line 450 "smarts.yy"
                                                {
   // this is a recursive SMARTS expression
   QueryAtom *qA = new QueryAtom();
@@ -2250,11 +2261,11 @@ yyreduce:
   }
   (yyval.atom) = qA;
 }
-#line 2254 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2265 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 46:
-#line 458 "smarts.yy"
+#line 467 "smarts.yy"
                                                                 {
   // UNDOCUMENTED EXTENSION:
   // this is a recursive SMARTS expression with a serial number
@@ -2276,117 +2287,117 @@ yyreduce:
   }
   (yyval.atom) = qA;
 }
-#line 2280 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2291 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 48:
-#line 483 "smarts.yy"
+#line 492 "smarts.yy"
                      {
   (yyvsp[0].atom)->setIsotope((yyvsp[-1].ival));
   (yyvsp[0].atom)->expandQuery(makeAtomIsotopeQuery((yyvsp[-1].ival)),Queries::COMPOSITE_AND,true);
   (yyval.atom)=(yyvsp[0].atom);
 }
-#line 2290 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2301 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 50:
-#line 489 "smarts.yy"
+#line 498 "smarts.yy"
                     {
   (yyvsp[0].atom)->setIsotope((yyvsp[-1].ival));
   (yyvsp[0].atom)->expandQuery(makeAtomIsotopeQuery((yyvsp[-1].ival)),Queries::COMPOSITE_AND,true);
   (yyval.atom)=(yyvsp[0].atom);
 }
-#line 2300 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2311 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 51:
-#line 494 "smarts.yy"
+#line 503 "smarts.yy"
                     { (yyval.atom) = new QueryAtom((yyvsp[0].ival)); }
-#line 2306 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2317 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 52:
-#line 495 "smarts.yy"
+#line 504 "smarts.yy"
                            {
   (yyval.atom) = new QueryAtom((yyvsp[0].ival));
   (yyval.atom)->setIsotope((yyvsp[-2].ival));
   (yyval.atom)->expandQuery(makeAtomIsotopeQuery((yyvsp[-2].ival)),Queries::COMPOSITE_AND,true);
 }
-#line 2316 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2327 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 59:
-#line 506 "smarts.yy"
+#line 515 "smarts.yy"
                                   {
   static_cast<ATOM_EQUALS_QUERY *>((yyvsp[-1].atom)->getQuery())->setVal((yyvsp[0].ival));
 }
-#line 2324 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2335 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 60:
-#line 509 "smarts.yy"
+#line 518 "smarts.yy"
                                          {
   (yyvsp[-1].atom)->setQuery(makeAtomNumHeteroatomNbrsQuery((yyvsp[0].ival)));
 }
-#line 2332 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2343 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 61:
-#line 512 "smarts.yy"
+#line 521 "smarts.yy"
                                                   {
   (yyvsp[-1].atom)->setQuery(makeAtomNumAliphaticHeteroatomNbrsQuery((yyvsp[0].ival)));
 }
-#line 2340 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2351 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 62:
-#line 515 "smarts.yy"
+#line 524 "smarts.yy"
                                    {
   (yyvsp[-1].atom)->setQuery(makeAtomMinRingSizeQuery((yyvsp[0].ival)));
 }
-#line 2348 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2359 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 63:
-#line 518 "smarts.yy"
+#line 527 "smarts.yy"
                                    {
   (yyvsp[-1].atom)->setQuery(makeAtomRingBondCountQuery((yyvsp[0].ival)));
 }
-#line 2356 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2367 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 64:
-#line 521 "smarts.yy"
+#line 530 "smarts.yy"
                                      {
   (yyvsp[-1].atom)->setQuery(makeAtomImplicitHCountQuery((yyvsp[0].ival)));
 }
-#line 2364 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2375 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 65:
-#line 524 "smarts.yy"
+#line 533 "smarts.yy"
                                                                              {
   ATOM_EQUALS_QUERY *oq = static_cast<ATOM_EQUALS_QUERY *>((yyvsp[-4].atom)->getQuery());
   ATOM_GREATEREQUAL_QUERY *nq = makeAtomSimpleQuery<ATOM_GREATEREQUAL_QUERY>((yyvsp[-1].ival),oq->getDataFunc(),
     std::string("greater_")+oq->getDescription());
   (yyvsp[-4].atom)->setQuery(nq);
 }
-#line 2375 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2386 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 66:
-#line 530 "smarts.yy"
+#line 539 "smarts.yy"
                                                                              {
   ATOM_EQUALS_QUERY *oq = static_cast<ATOM_EQUALS_QUERY *>((yyvsp[-4].atom)->getQuery());
   ATOM_LESSEQUAL_QUERY *nq = makeAtomSimpleQuery<ATOM_LESSEQUAL_QUERY>((yyvsp[-2].ival),oq->getDataFunc(),
     std::string("less_")+oq->getDescription());
   (yyvsp[-4].atom)->setQuery(nq);
 }
-#line 2386 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2397 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 67:
-#line 536 "smarts.yy"
+#line 545 "smarts.yy"
                                                                                {
   ATOM_EQUALS_QUERY *oq = static_cast<ATOM_EQUALS_QUERY *>((yyvsp[-5].atom)->getQuery());
   ATOM_RANGE_QUERY *nq = makeAtomRangeQuery((yyvsp[-3].ival),(yyvsp[-1].ival),false,false,
@@ -2394,11 +2405,11 @@ yyreduce:
     std::string("range_")+oq->getDescription());
   (yyvsp[-5].atom)->setQuery(nq);
 }
-#line 2398 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2409 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 68:
-#line 543 "smarts.yy"
+#line 552 "smarts.yy"
                  {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomIsotopeQuery((yyvsp[-1].ival)));
@@ -2407,11 +2418,11 @@ yyreduce:
   newQ->setNumExplicitHs(1);
   (yyval.atom)=newQ;
 }
-#line 2411 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2422 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 69:
-#line 551 "smarts.yy"
+#line 560 "smarts.yy"
                         {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomIsotopeQuery((yyvsp[-2].ival)));
@@ -2420,136 +2431,136 @@ yyreduce:
   newQ->setNumExplicitHs((yyvsp[0].ival));
   (yyval.atom)=newQ;
 }
-#line 2424 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2435 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 70:
-#line 559 "smarts.yy"
+#line 568 "smarts.yy"
                  {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomHCountQuery((yyvsp[0].ival)));
   newQ->setNumExplicitHs((yyvsp[0].ival));
   (yyval.atom)=newQ;
 }
-#line 2435 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2446 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 71:
-#line 565 "smarts.yy"
+#line 574 "smarts.yy"
           {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomHCountQuery(1));
   newQ->setNumExplicitHs(1);
   (yyval.atom)=newQ;
 }
-#line 2446 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2457 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 72:
-#line 571 "smarts.yy"
+#line 580 "smarts.yy"
               {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomFormalChargeQuery((yyvsp[0].ival)));
   newQ->setFormalCharge((yyvsp[0].ival));
   (yyval.atom)=newQ;
 }
-#line 2457 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2468 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 73:
-#line 577 "smarts.yy"
+#line 586 "smarts.yy"
                     {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomNullQuery());
   newQ->setChiralTag(Atom::CHI_TETRAHEDRAL_CW);
   (yyval.atom)=newQ;
 }
-#line 2468 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2479 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 74:
-#line 583 "smarts.yy"
+#line 592 "smarts.yy"
            {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomNullQuery());
   newQ->setChiralTag(Atom::CHI_TETRAHEDRAL_CCW);
   (yyval.atom)=newQ;
 }
-#line 2479 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2490 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 76:
-#line 590 "smarts.yy"
+#line 599 "smarts.yy"
          {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomIsotopeQuery((yyvsp[0].ival)));
   (yyval.atom)=newQ;
 }
-#line 2489 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2500 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 78:
-#line 598 "smarts.yy"
+#line 607 "smarts.yy"
                                   {
   (yyvsp[0].atom)->setQuery(makeAtomNumHeteroatomNbrsQuery(0));
 }
-#line 2497 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2508 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 79:
-#line 601 "smarts.yy"
+#line 610 "smarts.yy"
                                            {
   (yyvsp[0].atom)->setQuery(makeAtomNumAliphaticHeteroatomNbrsQuery(0));
 }
-#line 2505 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2516 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 80:
-#line 604 "smarts.yy"
+#line 613 "smarts.yy"
                             {
   (yyvsp[0].atom)->setQuery(makeAtomMinRingSizeQuery(5)); // this is going to be ignored anyway
 }
-#line 2513 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2524 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 81:
-#line 607 "smarts.yy"
+#line 616 "smarts.yy"
                             {
   (yyvsp[0].atom)->setQuery(makeAtomRingBondCountQuery(0));
 }
-#line 2521 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2532 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 82:
-#line 610 "smarts.yy"
+#line 619 "smarts.yy"
                               {
   (yyvsp[0].atom)->setQuery(makeAtomImplicitHCountQuery(0));
 }
-#line 2529 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2540 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 83:
-#line 613 "smarts.yy"
+#line 622 "smarts.yy"
              {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomFormalChargeQuery(0));
   (yyval.atom) = newQ;
 }
-#line 2539 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2550 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 84:
-#line 618 "smarts.yy"
+#line 627 "smarts.yy"
               {
   QueryAtom *newQ = new QueryAtom();
   newQ->setQuery(makeAtomNegativeFormalChargeQuery(0));
   (yyval.atom) = newQ;
 }
-#line 2549 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2560 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 85:
-#line 626 "smarts.yy"
+#line 635 "smarts.yy"
                                    {
   //
   // This construction (and some others) may seem odd, but the
@@ -2562,193 +2573,193 @@ yyreduce:
   (yyval.atom) = new QueryAtom((yyvsp[0].ival));
   (yyval.atom)->setQuery(makeAtomTypeQuery((yyvsp[0].ival),false));
 }
-#line 2566 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2577 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 86:
-#line 638 "smarts.yy"
+#line 647 "smarts.yy"
                       {
   (yyval.atom) = new QueryAtom((yyvsp[0].ival));
   (yyval.atom)->setIsAromatic(true);
   (yyval.atom)->setQuery(makeAtomTypeQuery((yyvsp[0].ival),true));
 }
-#line 2576 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2587 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 88:
-#line 648 "smarts.yy"
+#line 657 "smarts.yy"
                                         {
   (yyvsp[-2].bond)->expandQuery((yyvsp[0].bond)->getQuery()->copy(),Queries::COMPOSITE_AND,true);
   delete (yyvsp[0].bond);
 }
-#line 2585 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2596 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 89:
-#line 652 "smarts.yy"
+#line 661 "smarts.yy"
                                {
   (yyvsp[-2].bond)->expandQuery((yyvsp[0].bond)->getQuery()->copy(),Queries::COMPOSITE_OR,true);
   delete (yyvsp[0].bond);
 }
-#line 2594 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2605 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 90:
-#line 656 "smarts.yy"
+#line 665 "smarts.yy"
                                  {
   (yyvsp[-2].bond)->expandQuery((yyvsp[0].bond)->getQuery()->copy(),Queries::COMPOSITE_AND,true);
   delete (yyvsp[0].bond);
 }
-#line 2603 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2614 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 93:
-#line 664 "smarts.yy"
+#line 673 "smarts.yy"
                    {
   (yyvsp[-1].bond)->expandQuery((yyvsp[0].bond)->getQuery()->copy(),Queries::COMPOSITE_AND,true);
   delete (yyvsp[0].bond);
 }
-#line 2612 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2623 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 95:
-#line 672 "smarts.yy"
+#line 681 "smarts.yy"
               {
   QueryBond *newB= new QueryBond();
   newB->setBondType(Bond::SINGLE);
   newB->setQuery(makeBondOrderEqualsQuery(Bond::SINGLE));
   (yyval.bond) = newB;
 }
-#line 2623 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2634 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 96:
-#line 678 "smarts.yy"
+#line 687 "smarts.yy"
              {
   QueryBond *newB= new QueryBond();
   newB->setBondType(Bond::TRIPLE);
   newB->setQuery(makeBondOrderEqualsQuery(Bond::TRIPLE));
   (yyval.bond) = newB;
 }
-#line 2634 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2645 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 97:
-#line 684 "smarts.yy"
+#line 693 "smarts.yy"
               {
   QueryBond *newB= new QueryBond();
   newB->setBondType(Bond::AROMATIC);
   newB->setQuery(makeBondOrderEqualsQuery(Bond::AROMATIC));
   (yyval.bond) = newB;
 }
-#line 2645 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2656 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 98:
-#line 690 "smarts.yy"
+#line 699 "smarts.yy"
            {
   QueryBond *newB= new QueryBond();
   newB->setQuery(makeBondIsInRingQuery());
   (yyval.bond) = newB;
 }
-#line 2655 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2666 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 99:
-#line 695 "smarts.yy"
+#line 704 "smarts.yy"
                   {
   (yyvsp[0].bond)->getQuery()->setNegation(!((yyvsp[0].bond)->getQuery()->getNegation()));
   (yyval.bond) = (yyvsp[0].bond);
 }
-#line 2664 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2675 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 100:
-#line 702 "smarts.yy"
+#line 711 "smarts.yy"
                                    { (yyval.ival)=2; }
-#line 2670 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2681 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 101:
-#line 703 "smarts.yy"
+#line 712 "smarts.yy"
                     { (yyval.ival)=(yyvsp[0].ival); }
-#line 2676 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2687 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 102:
-#line 704 "smarts.yy"
+#line 713 "smarts.yy"
              { (yyval.ival)=1; }
-#line 2682 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2693 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 103:
-#line 705 "smarts.yy"
+#line 714 "smarts.yy"
                           { (yyval.ival)=-2; }
-#line 2688 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2699 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 104:
-#line 706 "smarts.yy"
+#line 715 "smarts.yy"
                      { (yyval.ival)=-(yyvsp[0].ival); }
-#line 2694 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2705 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 105:
-#line 707 "smarts.yy"
+#line 716 "smarts.yy"
               { (yyval.ival)=-1; }
-#line 2700 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2711 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 107:
-#line 712 "smarts.yy"
+#line 721 "smarts.yy"
                                           { (yyval.ival) = (yyvsp[-1].ival)*10+(yyvsp[0].ival); }
-#line 2706 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2717 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 108:
-#line 713 "smarts.yy"
+#line 722 "smarts.yy"
                                                          { (yyval.ival) = (yyvsp[-1].ival); }
-#line 2712 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2723 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 109:
-#line 714 "smarts.yy"
+#line 723 "smarts.yy"
                                                                { (yyval.ival) = (yyvsp[-2].ival)*10+(yyvsp[-1].ival); }
-#line 2718 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2729 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 110:
-#line 715 "smarts.yy"
+#line 724 "smarts.yy"
                                                                      { (yyval.ival) = (yyvsp[-3].ival)*100+(yyvsp[-2].ival)*10+(yyvsp[-1].ival); }
-#line 2724 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2735 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 111:
-#line 716 "smarts.yy"
+#line 725 "smarts.yy"
                                                                            { (yyval.ival) = (yyvsp[-4].ival)*1000+(yyvsp[-3].ival)*100+(yyvsp[-2].ival)*10+(yyvsp[-1].ival); }
-#line 2730 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2741 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 112:
-#line 717 "smarts.yy"
+#line 726 "smarts.yy"
                                                                                { (yyval.ival) = (yyvsp[-5].ival)*10000+(yyvsp[-4].ival)*1000+(yyvsp[-3].ival)*100+(yyvsp[-2].ival)*10+(yyvsp[-1].ival); }
-#line 2736 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2747 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

   case 116:
-#line 728 "smarts.yy"
+#line 737 "smarts.yy"
                        {
     if((yyvsp[-1].ival) >= std::numeric_limits<std::int32_t>::max()/10 ||
      (yyvsp[-1].ival)*10 >= std::numeric_limits<std::int32_t>::max()-(yyvsp[0].ival) ){
-     yysmarts_error(input,molList,lastAtom,lastBond,scanner,start_token,"number too large");
+     yysmarts_error(input,molList,lastAtom,lastBond,numAtomsParsed,numBondsParsed,scanner,start_token,"number too large");
      YYABORT;
   }
   (yyval.ival) = (yyvsp[-1].ival)*10 + (yyvsp[0].ival); }
-#line 2748 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2759 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
     break;

-#line 2752 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"
+#line 2763 "/scratch/RDKit_git/Code/GraphMol/SmilesParse/smarts.tab.cpp"

       default: break;
     }
@@ -2798,7 +2809,7 @@ yyerrlab:
     {
       ++yynerrs;
 #if ! YYERROR_VERBOSE
-      yyerror (input, molList, lastAtom, lastBond, scanner, start_token, YY_("syntax error"));
+      yyerror (input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token, YY_("syntax error"));
 #else
 # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
                                         yyssp, yytoken)
@@ -2825,7 +2836,7 @@ yyerrlab:
                 yymsgp = yymsg;
               }
           }
-        yyerror (input, molList, lastAtom, lastBond, scanner, start_token, yymsgp);
+        yyerror (input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token, yymsgp);
         if (yysyntax_error_status == 2)
           goto yyexhaustedlab;
       }
@@ -2849,7 +2860,7 @@ yyerrlab:
       else
         {
           yydestruct ("Error: discarding",
-                      yytoken, &yylval, input, molList, lastAtom, lastBond, scanner, start_token);
+                      yytoken, &yylval, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token);
           yychar = YYEMPTY;
         }
     }
@@ -2903,7 +2914,7 @@ yyerrlab1:

       yydestruct ("Error: popping",
-                  yystos[yystate], yyvsp, input, molList, lastAtom, lastBond, scanner, start_token);
+                  yystos[yystate], yyvsp, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token);
       YYPOPSTACK (1);
       yystate = *yyssp;
       YY_STACK_PRINT (yyss, yyssp);
@@ -2942,7 +2953,7 @@ yyabortlab:
 | yyexhaustedlab -- memory exhaustion comes here.  |
 `-------------------------------------------------*/
 yyexhaustedlab:
-  yyerror (input, molList, lastAtom, lastBond, scanner, start_token, YY_("memory exhausted"));
+  yyerror (input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token, YY_("memory exhausted"));
   yyresult = 2;
   /* Fall through.  */
 #endif
@@ -2958,7 +2969,7 @@ yyreturn:
          user semantic actions for why this is necessary.  */
       yytoken = YYTRANSLATE (yychar);
       yydestruct ("Cleanup: discarding lookahead",
-                  yytoken, &yylval, input, molList, lastAtom, lastBond, scanner, start_token);
+                  yytoken, &yylval, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token);
     }
   /* Do not reclaim the symbols of the rule whose action triggered
      this YYABORT or YYACCEPT.  */
@@ -2967,7 +2978,7 @@ yyreturn:
   while (yyssp != yyss)
     {
       yydestruct ("Cleanup: popping",
-                  yystos[+*yyssp], yyvsp, input, molList, lastAtom, lastBond, scanner, start_token);
+                  yystos[+*yyssp], yyvsp, input, molList, lastAtom, lastBond, numAtomsParsed, numBondsParsed, scanner, start_token);
       YYPOPSTACK (1);
     }
 #ifndef yyoverflow
@@ -2980,5 +2991,5 @@ yyreturn:
 #endif
   return yyresult;
 }
-#line 741 "smarts.yy"
+#line 750 "smarts.yy"

diff --git a/Code/GraphMol/SmilesParse/smarts.tab.hpp.cmake b/Code/GraphMol/SmilesParse/smarts.tab.hpp.cmake
index 46fb6115b4a..5584c6f9f47 100644
--- a/Code/GraphMol/SmilesParse/smarts.tab.hpp.cmake
+++ b/Code/GraphMol/SmilesParse/smarts.tab.hpp.cmake
@@ -99,7 +99,7 @@ extern int yysmarts_debug;
 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
 union YYSTYPE
 {
-#line 68 "smarts.yy"
+#line 65 "smarts.yy"

   int                      moli;
   RDKit::QueryAtom * atom;
@@ -116,9 +116,9 @@ typedef union YYSTYPE YYSTYPE;

-int yysmarts_parse (const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, void *scanner, int& start_token);
+int yysmarts_parse (const char *input, std::vector<RDKit::RWMol *> *molList, RDKit::Atom* &lastAtom, RDKit::Bond* &lastBond, unsigned &numAtomsParsed, unsigned &numBondsParsed, void *scanner, int& start_token);
 /* "%code provides" blocks.  */
-#line 63 "smarts.yy"
+#line 60 "smarts.yy"

 #define YY_DECL int yylex \
                (YYSTYPE * yylval_param , yyscan_t yyscanner, int& start_token)
diff --git a/Code/GraphMol/SmilesParse/smarts.yy b/Code/GraphMol/SmilesParse/smarts.yy
index 735e8486587..b1b2c3a5215 100644
--- a/Code/GraphMol/SmilesParse/smarts.yy
+++ b/Code/GraphMol/SmilesParse/smarts.yy
@@ -1,8 +1,7 @@
 %{

-  // $Id$
   //
-  //  Copyright (C) 2003-2018 Greg Landrum and Rational Discovery LLC
+  //  Copyright (C) 2003-2022 Greg Landrum and other RDKit contributors
   //
   //   @@ All Rights Reserved  @@
   //
@@ -36,15 +35,11 @@ namespace {
 void
 yysmarts_error( const char *input,
                 std::vector<RDKit::RWMol *> *ms,
-                RDKit::Atom* &lastAtom,
-                RDKit::Bond* &lastBond,
-		void *scanner,int start_token, const char * msg )
+                RDKit::Atom* &,
+                RDKit::Bond* &,
+                unsigned int &,unsigned int &,
+		void *,int , const char *msg  )
 {
-  RDUNUSED_PARAM(input);
-  RDUNUSED_PARAM(lastAtom);
-  RDUNUSED_PARAM(lastBond);
-  RDUNUSED_PARAM(scanner);
-  RDUNUSED_PARAM(start_token);
   yyErrorCleanup(ms);
   BOOST_LOG(rdErrorLog) << "SMARTS Parse Error: " << msg << " while parsing: " << input << std::endl;
 }
@@ -57,6 +52,8 @@ yysmarts_error( const char *input,
 %parse-param {std::vector<RDKit::RWMol *> *molList}
 %parse-param {RDKit::Atom* &lastAtom}
 %parse-param {RDKit::Bond* &lastBond}
+%parse-param {unsigned &numAtomsParsed}
+%parse-param {unsigned &numBondsParsed}
 %parse-param {void *scanner}
 %parse-param {int& start_token}

@@ -188,6 +185,7 @@ mol: atomd {
   newB->setOwningMol(mp);
   newB->setBeginAtomIdx(atomIdx1);
   newB->setEndAtomIdx(atomIdx2);
+  newB->setProp("_cxsmilesBondIdx",numBondsParsed++);
   mp->addBond(newB);
   delete newB;
   //delete $2;
@@ -209,6 +207,7 @@ mol: atomd {
     $2->setBeginAtomIdx(atomIdx1);
     $2->setEndAtomIdx(atomIdx2);
   }
+  $2->setProp("_cxsmilesBondIdx",numBondsParsed++);
   mp->addBond($2);
   delete $2;
 }
@@ -236,6 +235,9 @@ mol: atomd {
   newB->setOwningMol(mp);
   newB->setBeginAtomIdx(atom->getIdx());
   mp->setBondBookmark(newB,$2);
+  if(!(mp->getAllBondsWithBookmark($2).size()%2)){
+    newB->setProp("_cxsmilesBondIdx",numBondsParsed++);
+  }
   mp->setAtomBookmark(atom,$2);

   SmilesParseOps::CheckRingClosureBranchStatus(atom,mp);
@@ -256,6 +258,7 @@ mol: atomd {
   mp->setBondBookmark($2,$3);
   $2->setOwningMol(mp);
   $2->setBeginAtomIdx(atom->getIdx());
+  $2->setProp("_cxsmilesBondIdx",numBondsParsed++);
   mp->setAtomBookmark(atom,$3);

   SmilesParseOps::CheckRingClosureBranchStatus(atom,mp);
@@ -271,9 +274,15 @@ mol: atomd {

 | mol branch {
   RWMol *m1_p = (*molList)[$$],*m2_p=(*molList)[$2];
+  unsigned int origNumAts = m1_p->getNumAtoms();
+  Atom *active = m1_p->getActiveAtom();
   // FIX: handle generic bonds here
   SmilesParseOps::AddFragToMol(m1_p,m2_p,Bond::UNSPECIFIED,Bond::NONE);
   delete m2_p;
+  Bond *bond = m1_p->getBondBetweenAtoms(active->getIdx(),origNumAts);
+  if(bond){
+    bond->setProp("_cxsmilesBondIdx",numBondsParsed++);
+  }
   int sz = molList->size();
   if ( sz==$2+1) {
     molList->resize( sz-1 );
@@ -728,7 +737,7 @@ nonzero_number:  NONZERO_DIGIT_TOKEN
 | nonzero_number digit {
     if($1 >= std::numeric_limits<std::int32_t>::max()/10 ||
      $1*10 >= std::numeric_limits<std::int32_t>::max()-$2 ){
-     yysmarts_error(input,molList,lastAtom,lastBond,scanner,start_token,"number too large");
+     yysmarts_error(input,molList,lastAtom,lastBond,numAtomsParsed,numBondsParsed,scanner,start_token,"number too large");
      YYABORT;
   }
   $$ = $1*10 + $2; }

diff --git a/Code/GraphMol/SmilesParse/catch_tests.cpp b/Code/GraphMol/SmilesParse/catch_tests.cpp
index 9e0848b905b..01300fdcf12 100644
--- a/Code/GraphMol/SmilesParse/catch_tests.cpp
+++ b/Code/GraphMol/SmilesParse/catch_tests.cpp
@@ -1854,4 +1854,33 @@ TEST_CASE(
         "SingleOrAromaticBond");
   CHECK(m->getBondWithIdx(1)->getQuery()->getDescription() ==
         "SingleOrAromaticBond");
+}
+
+TEST_CASE("Github #4878: cannot parse coordinate bonds from CXSMARTS",
+          "[cxsmiles]") {
+  SECTION("basics") {
+    auto m = "[Fe]OC |C:1.0|"_smarts;
+    REQUIRE(m);
+    CHECK(m->getBondWithIdx(0)->getBondType() == Bond::BondType::DATIVE);
+  }
+  SECTION("branches") {
+    {
+      auto m = "[Fe](O)OC |C:2.1|"_smarts;
+      REQUIRE(m);
+      CHECK(m->getBondWithIdx(1)->getBondType() == Bond::BondType::DATIVE);
+    }
+    {
+      auto m = "[Fe](OC)O |C:1.0|"_smarts;
+      REQUIRE(m);
+      CHECK(m->getBondWithIdx(0)->getBondType() == Bond::BondType::DATIVE);
+    }
+  }
+  SECTION("rings") {
+    {
+      auto m = "O1CC[Fe]1 |C:0.3|"_smarts;
+      REQUIRE(m);
+      CHECK(m->getBondBetweenAtoms(0, 3)->getBondType() ==
+            Bond::BondType::DATIVE);
+    }
+  }
 }
\ No newline at end of file