Subgraphs::DiscrimTuple FragCatalogEntry::getDiscrims() const { Subgraphs::DiscrimTuple res; if (this->hasProp(common_properties::Discrims)) { this->getProp(common_properties::Discrims, res); } else { PATH_TYPE path; for (unsigned int i = 0; i < dp_mol->getNumBonds(); ++i) path.push_back(i); // create invariant additions to reflect the functional groups attached to // the atoms std::vector<std::uint32_t> funcGpInvars; gboost::hash<INT_VECT> vectHasher; for (ROMol::AtomIterator atomIt = dp_mol->beginAtoms(); atomIt != dp_mol->endAtoms(); ++atomIt) { unsigned int aid = (*atomIt)->getIdx(); std::uint32_t invar = 0; auto mapPos = d_aToFmap.find(aid); if (mapPos != d_aToFmap.end()) { INT_VECT fGroups = mapPos->second; std::sort(fGroups.begin(), fGroups.end()); invar = vectHasher(fGroups); } funcGpInvars.push_back(invar); } res = Subgraphs::calcPathDiscriminators(*dp_mol, path, true, &funcGpInvars); this->setProp(common_properties::Discrims, res); } // std::cout << "DISCRIMS: " << d_descrip << " "; // std::cout << res.get<0>() << " " << res.get<1>() << " " << res.get<2>(); // std::cout << std::endl; return res; }
FragCatalogEntry::FragCatalogEntry(const ROMol *omol, const PATH_TYPE &path, const MatchVectType &aidToFid) { PRECONDITION(omol, "bad mol"); // start with the assumption that this entry is not participating in // any find of fingerprinting d_aToFmap.clear(); setBitId(-1); INT_MAP_INT aIdxMap; // a map from atom id in omol to the new atoms id in mol dp_mol = Subgraphs::pathToSubmol(*omol, path, false, aIdxMap); // Using Subgraphs functionality d_order = path.size(); // using aIdxMap initialize the location (and their IDs) of the // functional groups on dp_mol for (const auto &mvtci : aidToFid) { int oldAid = mvtci.first; if (aIdxMap.find(oldAid) != aIdxMap.end()) { int newAid = aIdxMap[oldAid]; if (d_aToFmap.find(newAid) != d_aToFmap.end()) { d_aToFmap[newAid].push_back(mvtci.second); } else { INT_VECT tmpVect; tmpVect.clear(); tmpVect.push_back(mvtci.second); d_aToFmap[newAid] = tmpVect; } } } dp_props = new Dict(); d_descrip = ""; }
void FragCatalogEntry::toStream(std::ostream &ss) const { MolPickler::pickleMol(*dp_mol, ss); std::int32_t tmpInt; tmpInt = getBitId(); streamWrite(ss, tmpInt); tmpInt = d_descrip.size(); streamWrite(ss, tmpInt); ss.write(d_descrip.c_str(), tmpInt * sizeof(char)); tmpInt = d_order; streamWrite(ss, tmpInt); tmpInt = d_aToFmap.size(); streamWrite(ss, tmpInt); for (const auto &iivmci : d_aToFmap) { tmpInt = iivmci.first; streamWrite(ss, tmpInt); INT_VECT tmpVect = iivmci.second; tmpInt = tmpVect.size(); streamWrite(ss, tmpInt); for (INT_VECT_CI ivci = tmpVect.begin(); ivci != tmpVect.end(); ivci++) { tmpInt = *ivci; streamWrite(ss, tmpInt); } } }
void makeRingNeighborMap(const VECT_INT_VECT &brings, INT_INT_VECT_MAP &neighMap, unsigned int maxSize) { int nrings = rdcast<int>(brings.size()); int i, j; INT_VECT ring1; for (i = 0; i < nrings; i++) { INT_VECT neighs; neighMap[i] = neighs; } for (i = 0; i < nrings; i++) { if (maxSize && brings[i].size() > maxSize) continue; ring1 = brings[i]; for (j = i + 1; j < nrings; j++) { if (maxSize && brings[j].size() > maxSize) continue; INT_VECT inter; Intersect(ring1, brings[j], inter); if (inter.size() > 0) { neighMap[i].push_back(j); neighMap[j].push_back(i); } } } #if 0 for (i = 0; i < nrings; i++) { std::cerr<<"**************\n "<<i<<"\n*************\n"; std::copy(neighMap[i].begin(),neighMap[i].end(),std::ostream_iterator<int>(std::cerr," ")); std::cerr<<"\n"; } #endif }
const std::string GetMolFileQueryInfo(const RWMol &mol){ std::stringstream ss; boost::dynamic_bitset<> listQs(mol.getNumAtoms()); for(ROMol::ConstAtomIterator atomIt=mol.beginAtoms(); atomIt!=mol.endAtoms();++atomIt){ if(hasListQuery(*atomIt)) listQs.set((*atomIt)->getIdx()); } for(ROMol::ConstAtomIterator atomIt=mol.beginAtoms(); atomIt!=mol.endAtoms();++atomIt){ if(!listQs[(*atomIt)->getIdx()] && hasComplexQuery(*atomIt)){ std::string sma=SmartsWrite::GetAtomSmarts(static_cast<const QueryAtom *>(*atomIt)); ss<< "V "<<std::setw(3)<<(*atomIt)->getIdx()+1<<" "<<sma<<std::endl; } } for(ROMol::ConstAtomIterator atomIt=mol.beginAtoms(); atomIt!=mol.endAtoms();++atomIt){ if(listQs[(*atomIt)->getIdx()]){ INT_VECT vals; getListQueryVals((*atomIt)->getQuery(),vals); ss<<"M ALS "<<std::setw(3)<<(*atomIt)->getIdx()+1<<" "; ss<<std::setw(2)<<vals.size(); if((*atomIt)->getQuery()->getNegation()){ ss<<" T"; } else { ss<<" F"; } BOOST_FOREACH(int val,vals){ ss<<" "<<std::setw(3)<<std::left<<(PeriodicTable::getTable()->getElementSymbol(val)); } ss<<"\n"; } }
void FragCatalogEntry::setDescription(const FragCatParams *params) { PRECONDITION(params, ""); INT_INT_VECT_MAP::const_iterator fMapIt; for (fMapIt = d_aToFmap.begin(); fMapIt != d_aToFmap.end(); fMapIt++) { int atIdx = fMapIt->first; INT_VECT fGroups = fMapIt->second; std::string label = "", temp; INT_VECT::const_iterator fGroupIdx = fGroups.begin(); const ROMol *fGroup; for (unsigned int i = 0; i < fGroups.size() - 1; i++) { fGroup = params->getFuncGroup(*fGroupIdx); fGroup->getProp(common_properties::_Name, temp); label += "(<" + temp + ">)"; fGroupIdx++; } fGroup = params->getFuncGroup(*fGroupIdx); fGroup->getProp(common_properties::_Name, temp); label += "<" + temp + ">"; dp_mol->getAtomWithIdx(atIdx) ->setProp(common_properties::_supplementalSmilesLabel, label); } std::string smi = MolToSmiles(*dp_mol); // std::cerr << "----" << smi << "----" << std::endl; d_descrip = smi; };
INT_VECT GetBitFuncGroupIds(const FragCatalog *self,unsigned int idx){ if(idx > self->getFPLength()) throw_index_error(idx); INT_VECT res; INT_INT_VECT_MAP gps = self->getEntryWithBitId(idx)->getFuncGroupMap(); for(INT_INT_VECT_MAP::const_iterator i=gps.begin();i!=gps.end();i++){ for(INT_VECT_CI ivci=i->second.begin();ivci!=i->second.end();ivci++){ res.push_back(*ivci); } } return res; }
INT_VECT next() { INT_VECT res; if (d_pos >= (0x1u << d_questions.size())) { return res; } for (unsigned int i = 0; i < d_questions.size(); ++i) { if (d_pos & (0x1u << i)) { res.push_back(d_questions[i]); } } ++d_pos; return res; };
void kekulizeFused(RWMol &mol, const VECT_INT_VECT ås, unsigned int maxBackTracks) { // get all the atoms in the ring system INT_VECT allAtms; Union(arings, allAtms); // get all the atoms that are candidates to receive a double bond // also mark atoms in the fused system that are not aromatic to begin with // as done. Mark all the bonds that are part of the aromatic system // to be single bonds INT_VECT done; INT_VECT questions; unsigned int nats = mol.getNumAtoms(); unsigned int nbnds = mol.getNumBonds(); boost::dynamic_bitset<> dBndCands(nats); boost::dynamic_bitset<> dBndAdds(nbnds); markDbondCands(mol, allAtms, dBndCands, questions, done); #if 0 std::cerr << "candidates: "; for(int i=0;i<nats;++i) std::cerr << dBndCands[i]; std::cerr << std::endl; #endif bool kekulized; kekulized = kekulizeWorker(mol, allAtms, dBndCands, dBndAdds, done, maxBackTracks); if (!kekulized && questions.size()) { // we failed, but there are some dummy atoms we can try permuting. kekulized = permuteDummiesAndKekulize(mol, allAtms, dBndCands, questions, maxBackTracks); } if (!kekulized) { // we exhausted all option (or crossed the allowed // number of backTracks) and we still need to backtrack // can't kekulize this thing std::ostringstream errout; errout << "Can't kekulize mol."; errout << " Unkekulized atoms:"; for (unsigned int i = 0; i < nats; ++i) { if (dBndCands[i]) errout << " " << i; } errout << std::endl; std::string msg = errout.str(); BOOST_LOG(rdErrorLog) << msg << std::endl; throw MolSanitizeException(msg); } }
int nextCombination(INT_VECT &comb, int tot) { int nelem = static_cast<int>(comb.size()); int celem = nelem - 1; while (comb[celem] == (tot - nelem + celem)) { celem--; if (celem < 0) { return -1; } } unsigned int i; comb[celem] += 1; for (i = celem+1; i < comb.size(); i++) { comb[i] = comb[i-1] + 1; } return celem; }
void Intersect(const INT_VECT &r1, const INT_VECT &r2, INT_VECT &res) { res.resize(0); INT_VECT_CI ri; for (ri = r1.begin(); ri != r1.end(); ri++) { if (std::find(r2.begin(), r2.end(), (*ri)) != r2.end()){ res.push_back(*ri); } } }
void pickFusedRings(int curr, const INT_INT_VECT_MAP &neighMap, INT_VECT &res, boost::dynamic_bitset<> &done, int depth) { INT_INT_VECT_MAP::const_iterator pos = neighMap.find(curr); PRECONDITION(pos != neighMap.end(), "bad argument"); done[curr] = 1; res.push_back(curr); const INT_VECT &neighs = pos->second; #if 0 std::cerr<<"depth: "<<depth<<" ring: "<<curr<<" size: "<<res.size()<<" neighs: "<<neighs.size()<<std::endl; std::cerr<<" "; std::copy(neighs.begin(),neighs.end(),std::ostream_iterator<int>(std::cerr," ")); std::cerr<<"\n"; #endif for (INT_VECT_CI ni = neighs.begin(); ni != neighs.end(); ++ni) { if (!done[*ni]) { pickFusedRings((*ni), neighMap, res, done, depth + 1); } } }
void Union(const INT_VECT &r1, const INT_VECT &r2, INT_VECT &res) { res.resize(0); res = r1; INT_VECT_CI ri; for (ri = r2.begin(); ri != r2.end(); ri++) { if (std::find(res.begin(), res.end(), (*ri)) == res.end()){ res.push_back(*ri); } } }
ROMol *prepareMol(const ROMol &mol, const FragCatParams *fparams, MatchVectType &aToFmap) { PRECONDITION(fparams,""); // get a mapping of the functional groups onto the molecule INT_VECT fgBonds; MatchVectType aidToFid = findFuncGroupsOnMol(mol, fparams, fgBonds); // get the core piece of molecule (i.e. the part of the molecule // without the functional groups). This basically the part of the molecule // that does not contain the function group bonds given by "fgBonds" INT_VECT cBonds; int bid, nbds = mol.getNumBonds(); for (bid = 0; bid < nbds; bid++) { if (std::find(fgBonds.begin(), fgBonds.end(), bid) == fgBonds.end()) { cBonds.push_back(bid); } } INT_MAP_INT aIdxMap; // a map from atom id in mol to the new atoms id in coreMol ROMol *coreMol = Subgraphs::pathToSubmol(mol, cBonds, false, aIdxMap); // now map the functional groups on mol to coreMol using aIdxMap MatchVectType::iterator mati; int newID; for (mati = aidToFid.begin(); mati != aidToFid.end(); mati++) { newID = aIdxMap[mati->first]; aToFmap.push_back(std::pair<int, int>(newID, mati->second)); } return coreMol; }
void FragCatalogEntry::initFromStream(std::istream &ss) { // the molecule: dp_mol = new ROMol(); MolPickler::molFromPickle(ss, *dp_mol); std::int32_t tmpInt; // the bitId: streamRead(ss, tmpInt); setBitId(tmpInt); // the description: streamRead(ss, tmpInt); auto *tmpText = new char[tmpInt + 1]; ss.read(tmpText, tmpInt * sizeof(char)); tmpText[tmpInt] = 0; d_descrip = tmpText; delete[] tmpText; streamRead(ss, tmpInt); d_order = tmpInt; // now the map: streamRead(ss, tmpInt); for (int i = 0; i < tmpInt; i++) { std::int32_t key, value, size; streamRead(ss, key); streamRead(ss, size); INT_VECT tmpVect; tmpVect.clear(); for (int j = 0; j < size; j++) { streamRead(ss, value); tmpVect.push_back(value); } d_aToFmap[key] = tmpVect; } }
/*! \brief split the formal charge across atoms of same type if we have a conjugated system * * This function is called before the charge equivalization iteration is started for the * Gasteiger charges. If any of the atom involved in conjugated system have formal charges * set on them, this charges is equally distributed across atoms of the same type in that * conjugated system. So for example the two nitrogens in the benzamidine system start the iteration * with equal charges of 0.5 */ void splitChargeConjugated(const ROMol &mol, DOUBLE_VECT &charges) { int aix; int natms = mol.getNumAtoms(); INT_VECT marker; INT_VECT_CI mci; int aax, yax; double formal; const Atom *at, *aat, *yat; for (aix = 0; aix < natms; aix++) { at = mol.getAtomWithIdx(aix); formal = at->getFormalCharge(); //std::cout << aix << " formal charges:" << formal << "\n"; marker.resize(0); if ((fabs(formal) > EPS_DOUBLE) && (fabs(charges[aix]) < EPS_DOUBLE) ) { marker.push_back(aix); ROMol::OEDGE_ITER bnd1, end1, bnd2, end2; boost::tie(bnd1,end1) = mol.getAtomBonds(at); while (bnd1 != end1) { if (mol[*bnd1]->getIsConjugated()) { aax = mol[*bnd1]->getOtherAtomIdx(aix); aat = mol.getAtomWithIdx(aax); boost::tie(bnd2,end2) = mol.getAtomBonds(aat); while (bnd2 != end2) { if ((*bnd1) != (*bnd2)) { if (mol[*bnd2]->getIsConjugated()) { yax = mol[*bnd2]->getOtherAtomIdx(aax); yat = mol.getAtomWithIdx(yax); if (at->getAtomicNum() == yat->getAtomicNum()) { formal += yat->getFormalCharge(); marker.push_back(yax); } } } bnd2++; } } bnd1++; } for (mci = marker.begin(); mci != marker.end(); mci++) { charges[*mci] = (formal/marker.size()); } } } /* for (aix = 0; aix < natms; aix++) { std::cout << "In splitter: " << " charges:" << charges[aix] << "\n"; }*/ }
bool permuteDummiesAndKekulize(RWMol &mol, const INT_VECT &allAtms, boost::dynamic_bitset<> dBndCands, INT_VECT &questions, unsigned int maxBackTracks) { boost::dynamic_bitset<> atomsInPlay(mol.getNumAtoms()); for (int allAtm : allAtms) { atomsInPlay[allAtm] = 1; } bool kekulized = false; QuestionEnumerator qEnum(questions); while (!kekulized && questions.size()) { boost::dynamic_bitset<> dBndAdds(mol.getNumBonds()); INT_VECT done; #if 1 // reset the state: all aromatic bonds are remarked to single: for (RWMol::BondIterator bi = mol.beginBonds(); bi != mol.endBonds(); ++bi) { if ((*bi)->getIsAromatic() && (*bi)->getBondType() != Bond::SINGLE && atomsInPlay[(*bi)->getBeginAtomIdx()] && atomsInPlay[(*bi)->getEndAtomIdx()]) { (*bi)->setBondType(Bond::SINGLE); } } #endif // pick a new permutation of the questionable atoms: const INT_VECT &switchOff = qEnum.next(); if (!switchOff.size()) break; boost::dynamic_bitset<> tCands = dBndCands; for (int it : switchOff) { tCands[it] = 0; } #if 0 std::cerr<<"permute: "; for (boost::dynamic_bitset<>::size_type i = 0; i < tCands.size(); ++i){ std::cerr << tCands[i]; } std::cerr<<std::endl; #endif // try kekulizing again: kekulized = kekulizeWorker(mol, allAtms, tCands, dBndAdds, done, maxBackTracks); } return kekulized; }
bool checkFused(const INT_VECT &rids, INT_INT_VECT_MAP &ringNeighs) { INT_INT_VECT_MAP_CI nci; int nrings = rdcast<int>(ringNeighs.size()); boost::dynamic_bitset<> done(nrings); int rid; INT_VECT fused; // mark all rings in the system other than those in rids as done for (nci = ringNeighs.begin(); nci != ringNeighs.end(); nci++) { rid = (*nci).first; if (std::find(rids.begin(), rids.end(), rid) == rids.end()) { done[rid] = 1; } } // then pick a fused system from the remaining (i.e. rids) // If the rings in rids are fused we should get back all of them // in fused // if we get a smaller number in fused then rids are not fused pickFusedRings(rids.front(), ringNeighs, fused, done); CHECK_INVARIANT(fused.size() <= rids.size(), ""); return (fused.size() == rids.size()); }
void Union(const VECT_INT_VECT &rings, INT_VECT &res, const INT_VECT *exclude) { res.resize(0); INT_VECT ring; unsigned int id; unsigned int nrings = static_cast<unsigned int>(rings.size()); INT_VECT_CI ri; for (id = 0; id < nrings; id++) { if (exclude) { if (std::find(exclude->begin(), exclude->end(), static_cast<int>(id)) != exclude->end()) { continue; } } ring = rings[id]; for (ri = ring.begin(); ri != ring.end(); ri++) { if (std::find(res.begin(), res.end(), (*ri)) == res.end()) { res.push_back(*ri); } } } }
void getListQueryVals(const Atom::QUERYATOM_QUERY *q,INT_VECT &vals){ // list queries are series of nested ors of AtomAtomicNum queries PRECONDITION(q,"bad query"); std::string descr=q->getDescription(); PRECONDITION(descr=="AtomOr","bad query"); if(descr=="AtomOr"){ for(Atom::QUERYATOM_QUERY::CHILD_VECT_CI cIt=q->beginChildren(); cIt!=q->endChildren();++cIt){ std::string descr=(*cIt)->getDescription(); CHECK_INVARIANT((descr=="AtomOr"||descr=="AtomAtomicNum"),"bad query"); // we don't allow negation of any children of the query: if(descr=="AtomOr"){ getListQueryVals((*cIt).get(),vals); } else if(descr=="AtomAtomicNum"){ vals.push_back(static_cast<ATOM_EQUALS_QUERY *>((*cIt).get())->getVal()); } } } }
void canonicalDFSTraversal(ROMol &mol,int atomIdx,int inBondIdx, std::vector<AtomColors> &colors, VECT_INT_VECT &cycles, INT_VECT &ranks, INT_VECT &cyclesAvailable, MolStack &molStack, INT_VECT &atomOrders, INT_VECT &bondVisitOrders, VECT_INT_VECT &atomRingClosures, std::vector<INT_LIST> &atomTraversalBondOrder, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols ){ PRECONDITION(colors.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(ranks.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(atomOrders.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(bondVisitOrders.size()>=mol.getNumBonds(),"vector too small"); PRECONDITION(atomRingClosures.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(atomTraversalBondOrder.size()>=mol.getNumAtoms(),"vector too small"); PRECONDITION(!bondsInPlay || bondsInPlay->size()>=mol.getNumBonds(),"bondsInPlay too small"); PRECONDITION(!bondSymbols || bondSymbols->size()>=mol.getNumBonds(),"bondSymbols too small"); int nAttached=0; Atom *atom = mol.getAtomWithIdx(atomIdx); INT_LIST directTravList,cycleEndList; molStack.push_back(MolStackElem(atom)); atomOrders[atom->getIdx()] = molStack.size(); colors[atomIdx] = GREY_NODE; // --------------------- // // Build the list of possible destinations from here // // --------------------- std::vector< PossibleType > possibles; possibles.resize(0); ROMol::OBOND_ITER_PAIR bondsPair = mol.getAtomBonds(atom); possibles.reserve(bondsPair.second-bondsPair.first); while(bondsPair.first != bondsPair.second){ BOND_SPTR theBond = mol[*(bondsPair.first)]; bondsPair.first++; if(bondsInPlay && !(*bondsInPlay)[theBond->getIdx()]) continue; if(inBondIdx<0 || theBond->getIdx() != static_cast<unsigned int>(inBondIdx)){ int otherIdx = theBond->getOtherAtomIdx(atomIdx); long rank=ranks[otherIdx]; // --------------------- // // things are a bit more complicated if we are sitting on a // ring atom we would like to traverse first to the // ring-closure atoms, then to atoms outside the ring first, // then to atoms in the ring that haven't already been visited // (non-ring-closure atoms). // // Here's how the black magic works: // - non-ring atom neighbors have their original ranks // - ring atom neighbors have this added to their ranks: // (Bond::OTHER - bondOrder)*MAX_NATOMS*MAX_NATOMS // - ring-closure neighbors lose a factor of: // (Bond::OTHER+1)*MAX_NATOMS*MAX_NATOMS // // This tactic biases us to traverse to non-ring neighbors first, // original ordering if bond orders are all equal... crafty, neh? // // --------------------- if( colors[otherIdx] == GREY_NODE ) { rank -= static_cast<int>(Bond::OTHER+1) * MAX_NATOMS*MAX_NATOMS; if(!bondSymbols){ rank += static_cast<int>(Bond::OTHER - theBond->getBondType()) * MAX_NATOMS; } else { const std::string &symb=(*bondSymbols)[theBond->getIdx()]; boost::uint32_t hsh=gboost::hash_range(symb.begin(),symb.end()); rank += (hsh%MAX_NATOMS) * MAX_NATOMS; } } else if( theBond->getOwningMol().getRingInfo()->numBondRings(theBond->getIdx()) ){ if(!bondSymbols){ rank += static_cast<int>(Bond::OTHER - theBond->getBondType()) * MAX_NATOMS*MAX_NATOMS; } else { const std::string &symb=(*bondSymbols)[theBond->getIdx()]; boost::uint32_t hsh=gboost::hash_range(symb.begin(),symb.end()); rank += (hsh%MAX_NATOMS)*MAX_NATOMS*MAX_NATOMS; } } possibles.push_back(PossibleType(rank,otherIdx,theBond.get())); } } // --------------------- // // Sort on ranks // // --------------------- std::sort(possibles.begin(),possibles.end(),_possibleComp); // --------------------- // // Now work the children // // --------------------- std::vector<MolStack> subStacks; for(std::vector<PossibleType>::iterator possiblesIt=possibles.begin(); possiblesIt!=possibles.end(); possiblesIt++){ MolStack subStack; #if 0 int possibleIdx = possiblesIt->second.first; Bond *bond = possiblesIt->second.second; #endif int possibleIdx = possiblesIt->get<1>(); Bond *bond = possiblesIt->get<2>(); Atom *otherAtom=mol.getAtomWithIdx(possibleIdx); unsigned int lowestRingIdx; INT_VECT::const_iterator cAIt; switch(colors[possibleIdx]){ case WHITE_NODE: // ----- // we haven't seen this node at all before // ----- // it might have some residual data from earlier calls, clean that up: if(otherAtom->hasProp("_TraversalBondIndexOrder")){ otherAtom->clearProp("_TraversalBondIndexOrder"); } directTravList.push_back(bond->getIdx()); subStack.push_back(MolStackElem(bond,atomIdx)); canonicalDFSTraversal(mol,possibleIdx,bond->getIdx(),colors, cycles,ranks,cyclesAvailable,subStack, atomOrders,bondVisitOrders,atomRingClosures,atomTraversalBondOrder, bondsInPlay,bondSymbols); subStacks.push_back(subStack); nAttached += 1; break; case GREY_NODE: // ----- // we've seen this, but haven't finished it (we're finishing a ring) // ----- cycleEndList.push_back(bond->getIdx()); cAIt=std::find(cyclesAvailable.begin(), cyclesAvailable.end(),1); if(cAIt==cyclesAvailable.end()){ throw ValueErrorException("Too many rings open at once. SMILES cannot be generated."); } lowestRingIdx = cAIt-cyclesAvailable.begin(); cyclesAvailable[lowestRingIdx] = 0; cycles[possibleIdx].push_back(lowestRingIdx); ++lowestRingIdx; bond->setProp("_TraversalRingClosureBond",lowestRingIdx); molStack.push_back(MolStackElem(bond, atom->getIdx())); molStack.push_back(MolStackElem(lowestRingIdx)); // we need to add this bond (which closes the ring) to the traversal list for the // other atom as well: atomTraversalBondOrder[otherAtom->getIdx()].push_back(bond->getIdx()); atomRingClosures[otherAtom->getIdx()].push_back(bond->getIdx()); break; default: // ----- // this node has been finished. don't do anything. // ----- break; } } INT_VECT &ringClosures=atomRingClosures[atom->getIdx()]; CHECK_INVARIANT(ringClosures.size()==cycles[atomIdx].size(), "ring closure mismatch"); for(unsigned int i=0;i<ringClosures.size();i++){ int ringIdx=cycles[atomIdx][i]; ringIdx += 1; molStack.push_back(MolStackElem(ringIdx)); } cycles[atomIdx].resize(0); MolStack::const_iterator ciMS; for(int i=0;i<nAttached;i++){ if(i<nAttached-1){ int branchIdx=0; if(subStacks[i].begin()->type==MOL_STACK_ATOM){ branchIdx=subStacks[i].begin()->obj.atom->getIdx(); } else if(subStacks[i].begin()->type==MOL_STACK_BOND){ branchIdx=-1*subStacks[i].begin()->obj.bond->getIdx(); } else { ASSERT_INVARIANT(0,"branch started with something other than an atom or bond"); } molStack.push_back(MolStackElem("(",branchIdx)); for(ciMS=subStacks[i].begin();ciMS!=subStacks[i].end();ciMS++){ molStack.push_back(*ciMS); switch(ciMS->type){ case MOL_STACK_ATOM: atomOrders[ciMS->obj.atom->getIdx()] = molStack.size(); break; case MOL_STACK_BOND: bondVisitOrders[ciMS->obj.bond->getIdx()] = molStack.size(); break; default: break; } } molStack.push_back(MolStackElem(")",branchIdx)); } else { for(ciMS=subStacks[i].begin();ciMS!=subStacks[i].end();ciMS++){ molStack.push_back(*ciMS); switch(ciMS->type){ case MOL_STACK_ATOM: atomOrders[ciMS->obj.atom->getIdx()] = molStack.size(); break; case MOL_STACK_BOND: bondVisitOrders[ciMS->obj.bond->getIdx()] = molStack.size(); break; default: break; } } } } //std::cerr<<"*****>>>>>> Traversal results for atom: "<<atom->getIdx()<<"> "; INT_LIST travList; // first push on the incoming bond: if(inBondIdx >= 0){ //std::cerr<<" "<<inBondIdx; travList.push_back(inBondIdx); } // ... ring closures that end here: for(INT_LIST_CI ilci=cycleEndList.begin();ilci!=cycleEndList.end();++ilci){ //std::cerr<<" ["<<*ilci<<"]"; travList.push_back(*ilci); } // ... ring closures that start here: // if(atom->hasProp("_TraversalBondIndexOrder")){ // INT_LIST indirectTravList; // atom->getProp("_TraversalBondIndexOrder",indirectTravList); // for(INT_LIST_CI ilci=indirectTravList.begin();ilci!=indirectTravList.end();++ilci){ // //std::cerr<<" ("<<*ilci<<")"; // travList.push_back(*ilci); // } // } BOOST_FOREACH(int ili,atomTraversalBondOrder[atom->getIdx()]){ travList.push_back(ili); }
void dfsBuildStack(ROMol &mol,int atomIdx,int inBondIdx, std::vector<AtomColors> &colors, VECT_INT_VECT &cycles, const UINT_VECT &ranks, INT_VECT &cyclesAvailable, MolStack &molStack, INT_VECT &atomOrders, INT_VECT &bondVisitOrders, VECT_INT_VECT &atomRingClosures, std::vector<INT_LIST> &atomTraversalBondOrder, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols ){ #if 0 std::cerr<<"traverse from atom: "<<atomIdx<<" via bond "<<inBondIdx<<" num cycles available: " <<std::count(cyclesAvailable.begin(),cyclesAvailable.end(),1)<<std::endl; #endif Atom *atom = mol.getAtomWithIdx(atomIdx); INT_LIST directTravList,cycleEndList; boost::dynamic_bitset<> seenFromHere(mol.getNumAtoms()); seenFromHere.set(atomIdx); molStack.push_back(MolStackElem(atom)); atomOrders[atom->getIdx()] = molStack.size(); colors[atomIdx] = GREY_NODE; INT_LIST travList; if(inBondIdx>=0) travList.push_back(inBondIdx); // --------------------- // // Add any ring closures // // --------------------- if(atomRingClosures[atomIdx].size()){ std::vector<unsigned int> ringsClosed; BOOST_FOREACH(int bIdx,atomRingClosures[atomIdx]){ travList.push_back(bIdx); Bond *bond = mol.getBondWithIdx(bIdx); seenFromHere.set(bond->getOtherAtomIdx(atomIdx)); unsigned int ringIdx; if(bond->getPropIfPresent(common_properties::_TraversalRingClosureBond, ringIdx)){ // this is end of the ring closure // we can just pull the ring index from the bond itself: molStack.push_back(MolStackElem(bond,atomIdx)); bondVisitOrders[bIdx]=molStack.size(); molStack.push_back(MolStackElem(ringIdx)); // don't make the ring digit immediately available again: we don't want to have the same // ring digit opening and closing rings on an atom. ringsClosed.push_back(ringIdx-1); } else { // this is the beginning of the ring closure, we need to come up with a ring index: INT_VECT::const_iterator cAIt=std::find(cyclesAvailable.begin(), cyclesAvailable.end(),1); if(cAIt==cyclesAvailable.end()){ throw ValueErrorException("Too many rings open at once. SMILES cannot be generated."); } unsigned int lowestRingIdx = cAIt-cyclesAvailable.begin(); cyclesAvailable[lowestRingIdx] = 0; ++lowestRingIdx; bond->setProp(common_properties::_TraversalRingClosureBond,lowestRingIdx); molStack.push_back(MolStackElem(lowestRingIdx)); } }
// finds cycles void dfsFindCycles(ROMol &mol,int atomIdx,int inBondIdx, std::vector<AtomColors> &colors, const UINT_VECT &ranks, INT_VECT &atomOrders, VECT_INT_VECT &atomRingClosures, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols ){ Atom *atom = mol.getAtomWithIdx(atomIdx); atomOrders.push_back(atomIdx); colors[atomIdx] = GREY_NODE; // --------------------- // // Build the list of possible destinations from here // // --------------------- std::vector< PossibleType > possibles; possibles.resize(0); ROMol::OBOND_ITER_PAIR bondsPair = mol.getAtomBonds(atom); possibles.reserve(bondsPair.second-bondsPair.first); while(bondsPair.first != bondsPair.second){ BOND_SPTR theBond = mol[*(bondsPair.first)]; bondsPair.first++; if(bondsInPlay && !(*bondsInPlay)[theBond->getIdx()]) continue; if(inBondIdx<0 || theBond->getIdx() != static_cast<unsigned int>(inBondIdx)){ int otherIdx = theBond->getOtherAtomIdx(atomIdx); long rank=ranks[otherIdx]; // --------------------- // // things are a bit more complicated if we are sitting on a // ring atom. we would like to traverse first to the // ring-closure atoms, then to atoms outside the ring first, // then to atoms in the ring that haven't already been visited // (non-ring-closure atoms). // // Here's how the black magic works: // - non-ring atom neighbors have their original ranks // - ring atom neighbors have this added to their ranks: // (MAX_BONDTYPE - bondOrder)*MAX_NATOMS*MAX_NATOMS // - ring-closure neighbors lose a factor of: // (MAX_BONDTYPE+1)*MAX_NATOMS*MAX_NATOMS // // This tactic biases us to traverse to non-ring neighbors first, // original ordering if bond orders are all equal... crafty, neh? // // --------------------- if( colors[otherIdx] == GREY_NODE ) { rank -= static_cast<int>(MAX_BONDTYPE+1) * MAX_NATOMS*MAX_NATOMS; if(!bondSymbols){ rank += static_cast<int>(MAX_BONDTYPE - theBond->getBondType()) * MAX_NATOMS; } else { const std::string &symb=(*bondSymbols)[theBond->getIdx()]; boost::uint32_t hsh=gboost::hash_range(symb.begin(),symb.end()); rank += (hsh%MAX_NATOMS) * MAX_NATOMS; } } else if( theBond->getOwningMol().getRingInfo()->numBondRings(theBond->getIdx()) ){ if(!bondSymbols){ rank += static_cast<int>(MAX_BONDTYPE - theBond->getBondType()) * MAX_NATOMS*MAX_NATOMS; } else { const std::string &symb=(*bondSymbols)[theBond->getIdx()]; boost::uint32_t hsh=gboost::hash_range(symb.begin(),symb.end()); rank += (hsh%MAX_NATOMS)*MAX_NATOMS*MAX_NATOMS; } } //std::cerr<<"aIdx: "<< atomIdx <<" p: "<<otherIdx<<" Rank: "<<ranks[otherIdx] <<" "<<colors[otherIdx]<<" "<<theBond->getBondType()<<" "<<rank<<std::endl; possibles.push_back(PossibleType(rank,otherIdx,theBond.get())); } } // --------------------- // // Sort on ranks // // --------------------- std::sort(possibles.begin(),possibles.end(),_possibleCompare()); // --------------------- // // Now work the children // // --------------------- for(std::vector<PossibleType>::iterator possiblesIt=possibles.begin(); possiblesIt!=possibles.end(); possiblesIt++){ int possibleIdx = possiblesIt->get<1>(); Bond *bond = possiblesIt->get<2>(); Atom *otherAtom=mol.getAtomWithIdx(possibleIdx); switch(colors[possibleIdx]){ case WHITE_NODE: // ----- // we haven't seen this node at all before, traverse // ----- dfsFindCycles(mol,possibleIdx,bond->getIdx(),colors, ranks,atomOrders, atomRingClosures, bondsInPlay,bondSymbols); break; case GREY_NODE: // ----- // we've seen this, but haven't finished it (we're finishing a ring) // ----- atomRingClosures[possibleIdx].push_back(bond->getIdx()); atomRingClosures[atomIdx].push_back(bond->getIdx()); break; default: // ----- // this node has been finished. don't do anything. // ----- break; } } colors[atomIdx] = BLACK_NODE; }
int setAromaticity(RWMol &mol) { // FIX: we will assume for now that if the input molecule came // with aromaticity information it is correct and we will not // touch it. Loop through the atoms and check if any atom has // arom stuff set. We may want check this more carefully later // and start from scratch if necessary ROMol::AtomIterator ai; for (ai = mol.beginAtoms(); ai != mol.endAtoms(); ai++) { if ((*ai)->getIsAromatic()) { // found aromatic info return -1; } } // first find the all the simple rings in the molecule VECT_INT_VECT srings; if (mol.getRingInfo()->isInitialized()) { srings = mol.getRingInfo()->atomRings(); } else { MolOps::symmetrizeSSSR(mol, srings); } int narom = 0; // loop over all the atoms in the rings that can be candidates // for aromaticity // Atoms are candidates if // - it is part of ring // - has one or more electron to donate or has empty p-orbitals int natoms = mol.getNumAtoms(); boost::dynamic_bitset<> acands(natoms); boost::dynamic_bitset<> aseen(natoms); VECT_EDON_TYPE edon(natoms); VECT_INT_VECT cRings; // holder for rings that are candidates for aromaticity for (VECT_INT_VECT_I vivi = srings.begin(); vivi != srings.end(); ++vivi) { bool allAromatic = true; for (INT_VECT_I ivi = (*vivi).begin(); ivi != (*vivi).end(); ++ivi) { if (aseen[*ivi]) { if (!acands[*ivi]) allAromatic = false; continue; } aseen[*ivi] = 1; Atom *at = mol.getAtomWithIdx(*ivi); // now that the atom is part of ring check if it can donate // electron or has empty orbitals. Record the donor type // information in 'edon' - we will need it when we get to // the Huckel rule later edon[*ivi] = getAtomDonorTypeArom(at); acands[*ivi] = isAtomCandForArom(at, edon[*ivi]); if (!acands[*ivi]) allAromatic = false; } if (allAromatic) { cRings.push_back((*vivi)); } } // first convert all rings to bonds ids VECT_INT_VECT brings; RingUtils::convertToBonds(cRings, brings, mol); // make the neighbor map for the rings // i.e. a ring is a neighbor a another candidate ring if // shares at least one bond // useful to figure out fused systems INT_INT_VECT_MAP neighMap; RingUtils::makeRingNeighborMap(brings, neighMap, maxFusedAromaticRingSize); // now loop over all the candidate rings and check the // huckel rule - of course paying attention to fused systems. INT_VECT doneRs; int curr = 0; int cnrs = rdcast<int>(cRings.size()); boost::dynamic_bitset<> fusDone(cnrs); INT_VECT fused; while (curr < cnrs) { fused.resize(0); RingUtils::pickFusedRings(curr, neighMap, fused, fusDone); applyHuckelToFused(mol, cRings, brings, fused, edon, neighMap, narom, 6); int rix; for (rix = 0; rix < cnrs; rix++) { if (!fusDone[rix]) { curr = rix; break; } } if (rix == cnrs) { break; } } mol.setProp(common_properties::numArom, narom, true); return narom; }
MatchVectType findFuncGroupsOnMol(const ROMol &mol, const FragCatParams *params, INT_VECT &fgBonds) { PRECONDITION(params,"bad params"); fgBonds.clear(); std::pair<int, int> amat; MatchVectType aidFgrps; std::vector<MatchVectType> fgpMatches; std::vector<MatchVectType>::const_iterator mati; MatchVectType::const_iterator mi; int aid; //const ROMol *fgrp; INT_VECT_CI bi; aidFgrps.clear(); int fid = 0; const MOL_SPTR_VECT &fgrps = params->getFuncGroups(); MOL_SPTR_VECT::const_iterator fgci; for (fgci = fgrps.begin(); fgci != fgrps.end(); fgci++) { const ROMol *fgrp = fgci->get(); std::string fname; (*fgci)->getProp(common_properties::_Name, fname); //std::cout << "Groups number: " << fname << "\n"; //(*fgci)->debugMol(std::cout); //mol->debugMol(std::cout); // match this functional group onto the molecule SubstructMatch(mol, *fgrp, fgpMatches); // loop over all the matches we get for this fgroup for (mati = fgpMatches.begin(); mati != fgpMatches.end(); mati++) { //FIX: we will assume that the first atom in fgrp is always the connection // atom amat = mati->front(); aid = amat.second; //FIX: is this correct - the second entry in the pair is the atom ID from mol // grab the list of atom Ids from mol that match the functional group INT_VECT bondIds, maids; for (mi = mati->begin(); mi != mati->end(); mi++) { maids.push_back(mi->second); } // create a list of bond IDs from these atom ID // these are the bond in mol that are part of portion that matches the // functional group bondIds = Subgraphs::bondListFromAtomList(mol, maids); // now check if all these bonds have been covered as part of larger // functional group that was dealt with earlier // FIX: obviously we assume here that the function groups in params // come in decreasing order of size. bool allDone = true; for (bi = bondIds.begin(); bi != bondIds.end(); bi++) { if (std::find(fgBonds.begin(), fgBonds.end(), (*bi)) == fgBonds.end()) { allDone = false; fgBonds.push_back(*bi); } } if (!allDone) { // this functional group mapping onto mol is not part of a larger func // group mapping so record it aidFgrps.push_back(std::pair<int, int>(aid, fid)); } } fid++; } return aidFgrps; }
bool kekulizeWorker(RWMol &mol, const INT_VECT &allAtms, boost::dynamic_bitset<> dBndCands, boost::dynamic_bitset<> dBndAdds, INT_VECT done, unsigned int maxBackTracks) { INT_DEQUE astack; INT_INT_DEQ_MAP options; int lastOpt = -1; boost::dynamic_bitset<> localBondsAdded(mol.getNumBonds()); // ok the algorithm goes something like this // - start with an atom that has been marked aromatic before // - check if it can have a double bond // - add its neighbors to the stack // - check if one of its neighbors can also have a double bond // - if yes add a double bond. // - if multiple neighbors can have double bonds - add them to a // options stack we may have to retrace out path if we chose the // wrong neighbor to add the double bond // - if double bond added update the candidates for double bond // - move to the next atom on the stack and repeat the process // - if an atom that can have multiple a double bond has no // neighbors that can take double bond - we made a mistake // earlier by picking a wrong candidate for double bond // - in this case back track to where we made the mistake int curr; INT_DEQUE btmoves; unsigned int numBT = 0; // number of back tracks so far while ((done.size() < allAtms.size()) || (astack.size() > 0)) { // pick a curr atom to work with if (astack.size() > 0) { curr = astack.front(); astack.pop_front(); } else { for (int allAtm : allAtms) { if (std::find(done.begin(), done.end(), allAtm) == done.end()) { curr = allAtm; break; } } } done.push_back(curr); // loop over the neighbors if we can add double bonds or // simply push them onto the stack INT_DEQUE opts; bool cCand = false; if (dBndCands[curr]) { cCand = true; } int ncnd; // if we are here because of backtracking if (options.find(curr) != options.end()) { opts = options[curr]; CHECK_INVARIANT(opts.size() > 0, ""); } else { RWMol::ADJ_ITER nbrIdx, endNbrs; boost::tie(nbrIdx, endNbrs) = mol.getAtomNeighbors(mol.getAtomWithIdx(curr)); while (nbrIdx != endNbrs) { // ignore if the neighbor has already been dealt with before if (std::find(done.begin(), done.end(), static_cast<int>(*nbrIdx)) != done.end()) { ++nbrIdx; continue; } // ignore if the neighbor is not part of the fused system if (std::find(allAtms.begin(), allAtms.end(), static_cast<int>(*nbrIdx)) == allAtms.end()) { ++nbrIdx; continue; } // if the neighbor is not on the stack add it if (std::find(astack.begin(), astack.end(), static_cast<int>(*nbrIdx)) == astack.end()) { astack.push_back(rdcast<int>(*nbrIdx)); } // check if the neighbor is also a candidate for a double bond // the refinement that we'll make to the candidate check we've already // done is to make sure that the bond is either flagged as aromatic // or involves a dummy atom. This was Issue 3525076. // This fix is not really 100% of the way there: a situation like // that for Issue 3525076 but involving a dummy atom in the cage // could lead to the same failure. The full fix would require // a fairly detailed analysis of all bonds in the molecule to determine // which of them is eligible to be converted. if (cCand && dBndCands[*nbrIdx] && (mol.getBondBetweenAtoms(curr, *nbrIdx)->getIsAromatic() || mol.getAtomWithIdx(curr)->getAtomicNum() == 0 || mol.getAtomWithIdx(*nbrIdx)->getAtomicNum() == 0)) { opts.push_back(rdcast<int>(*nbrIdx)); } // end of curr atoms can have a double bond ++nbrIdx; } // end of looping over neighbors } // now add a double bond from current to one of the neighbors if we can if (cCand) { if (opts.size() > 0) { ncnd = opts.front(); opts.pop_front(); Bond *bnd = mol.getBondBetweenAtoms(curr, ncnd); bnd->setBondType(Bond::DOUBLE); // remove current and the neighbor from the dBndCands list dBndCands[curr] = 0; dBndCands[ncnd] = 0; // add them to the list of bonds to which have been made double dBndAdds[bnd->getIdx()] = 1; localBondsAdded[bnd->getIdx()] = 1; // if this is an atom we previously visted and picked we // simply tried a different option now, overwrite the options // stored for this atoms if (options.find(curr) != options.end()) { if (opts.size() == 0) { options.erase(curr); btmoves.pop_back(); if (btmoves.size() > 0) { lastOpt = btmoves.back(); } else { lastOpt = -1; } } else { options[curr] = opts; } } else { // this is new atoms we are trying and have other // neighbors as options to add double bond store this to // the options stack, we may have made a mistake in // which one we chose and have to return here if (opts.size() > 0) { lastOpt = curr; btmoves.push_back(lastOpt); options[curr] = opts; } } } // end of adding a double bond else { // we have an atom that should be getting a double bond // but none of the neighbors can take one. Most likely // because of a wrong choice earlier so back track if ((lastOpt >= 0) && (numBT < maxBackTracks)) { // std::cerr << "PRE BACKTRACK" << std::endl; // mol.debugMol(std::cerr); backTrack(mol, options, lastOpt, done, astack, dBndCands, dBndAdds); // std::cerr << "POST BACKTRACK" << std::endl; // mol.debugMol(std::cerr); numBT++; } else { // undo any remaining changes we made while here // this was github #962 for (unsigned int bidx = 0; bidx < mol.getNumBonds(); ++bidx) { if (localBondsAdded[bidx]) { mol.getBondWithIdx(bidx)->setBondType(Bond::SINGLE); } } return false; } } // end of else try to backtrack } // end of curr atom atom being a cand for double bond } // end of while we are not done with all atoms return true; }
int main(){ RDLog::InitLogs(); Dict d; INT_VECT fooV; fooV.resize(3); BOOST_LOG(rdInfoLog) << "dict test" << std::endl; CHECK_INVARIANT(!d.hasVal("foo"),"bad init"); int x = 1; d.setVal("foo", x); CHECK_INVARIANT(d.hasVal("foo"),"should be there"); CHECK_INVARIANT(!d.hasVal("bar"),"bad other key"); int v,v2; d.getVal("foo",v); CHECK_INVARIANT(v==1,"bad val"); v2=d.getVal<int>("foo"); CHECK_INVARIANT(v2==v,"bad val"); d.setVal("bar",fooV); d.getVal("foo",v); CHECK_INVARIANT(v==1,"bad val"); v2=d.getVal<int>("foo"); CHECK_INVARIANT(v2==v,"bad val"); INT_VECT fooV2,fooV3; d.getVal("bar",fooV2); fooV3=d.getVal<INT_VECT>("bar"); CHECK_INVARIANT(fooV==fooV2,"bad getVal"); CHECK_INVARIANT(fooV2==fooV3,"bad getVal"); VECT_INT_VECT fooV4; fooV4.resize(3); CHECK_INVARIANT(!d.hasVal("baz"),"bad get"); d.setVal("baz",fooV4); CHECK_INVARIANT(d.hasVal("baz"),"bad get"); DictCon dc1; CHECK_INVARIANT(!dc1.getDict()->hasVal("foo"),"bad init"); int y = 1; dc1.getDict()->setVal("foo",y); CHECK_INVARIANT(dc1.getDict()->hasVal("foo"),"should be there"); CHECK_INVARIANT(!dc1.getDict()->hasVal("bar"),"bad other key"); dc1.getDict()->setVal("bar",fooV); dc1.getDict()->getVal("foo",v); CHECK_INVARIANT(v==1,"bad val"); dc1.getDict()->getVal("bar",fooV2); CHECK_INVARIANT(fooV==fooV2,"bad getVal"); fooV4.resize(3); CHECK_INVARIANT(!dc1.getDict()->hasVal("baz"),"bad get"); dc1.getDict()->setVal("baz",fooV4); CHECK_INVARIANT(dc1.getDict()->hasVal("baz"),"bad get"); dc1.getDict()->reset(); DictCon dc2=dc1; CHECK_INVARIANT(!dc2.getDict()->hasVal("foo"),"bad init"); int z = 1; dc2.getDict()->setVal("foo",z); CHECK_INVARIANT(dc2.getDict()->hasVal("foo"),"should be there"); CHECK_INVARIANT(!dc2.getDict()->hasVal("bar"),"bad other key"); dc2.getDict()->setVal("bar",fooV); dc2.getDict()->getVal("foo",v); CHECK_INVARIANT(v==1,"bad val"); dc2.getDict()->getVal("bar",fooV2); CHECK_INVARIANT(fooV==fooV2,"bad getVal"); fooV4.resize(3); CHECK_INVARIANT(!dc2.getDict()->hasVal("baz"),"bad get"); dc2.getDict()->setVal("baz",fooV4); CHECK_INVARIANT(dc2.getDict()->hasVal("baz"),"bad get"); DictCon dc3(dc2); CHECK_INVARIANT(dc3.getDict()->hasVal("foo"),"should be there"); dc3.getDict()->getVal("foo",v); CHECK_INVARIANT(v==1,"bad val"); dc3.getDict()->getVal("bar",fooV2); CHECK_INVARIANT(fooV==fooV2,"bad getVal"); fooV4.resize(3); CHECK_INVARIANT(dc3.getDict()->hasVal("baz"),"bad get"); CHECK_INVARIANT(dc3.getDict()->hasVal("foo"),"should be there"); dc3.getDict()->getVal("foo",v); CHECK_INVARIANT(v==1,"bad val"); dc3.getDict()->getVal("bar",fooV2); CHECK_INVARIANT(fooV==fooV2,"bad getVal"); fooV4.resize(3); CHECK_INVARIANT(dc3.getDict()->hasVal("baz"),"bad get"); testStringVals(); testVectToString(); return 0; }