#include <algorithm>
#include <numeric>
#include <queue>
#include "sketcherMinimizerAtom.h"
#include "sketcherMinimizer.h"
#include "sketcherMinimizerBond.h"
#include "sketcherMinimizerMaths.h"
#include "sketcherMinimizerRing.h"
using namespace std;
bool CIPAtom::operator<(const CIPAtom& rhs) const
{
assert(allParents.size() == rhs.allParents.size());
for (size_t i = 0; i < allParents.size(); i++) {
if (allParents[i]->atomicNumber > rhs.allParents[i]->atomicNumber) {
return true;
}
if (allParents[i]->atomicNumber < rhs.allParents[i]->atomicNumber) {
return false;
}
if ((*scores)[allParents[i]] < (*rhs.scores)[rhs.allParents[i]]) {
return true;
}
if ((*scores)[allParents[i]] > (*rhs.scores)[rhs.allParents[i]]) {
return false;
}
vector<int> meds = (*medals)[allParents[i]];
vector<int> meds2 = (*rhs.medals)[rhs.allParents[i]];
size_t s = (meds.size() < meds2.size()) ? meds.size() : meds2.size();
for (size_t mm = 0; mm < s; mm++) {
if (meds[mm] > meds2[mm]) {
return true;
}
if (meds[mm] < meds2[mm]) {
return false;
}
}
if (meds.size() > meds2.size()) {
return true;
}
if (meds2.size() > meds.size()) {
return false;
}
}
size_t siz = theseAtoms.size();
if (rhs.theseAtoms.size() < siz) {
siz = rhs.theseAtoms.size();
}
for (size_t i = 0; i < siz; i++) {
if (theseAtoms[i].first > rhs.theseAtoms[i].first) {
return true;
}
if (theseAtoms[i].first < rhs.theseAtoms[i].first) {
return false;
}
}
if (theseAtoms.size() > rhs.theseAtoms.size()) {
return true;
}
if (theseAtoms.size() < rhs.theseAtoms.size()) {
return false;
}
return false;
}
bool CIPAtom::operator==(const CIPAtom& rhs) const
{
assert(allParents.size() == rhs.allParents.size());
for (size_t i = 0; i < allParents.size(); i++) {
if (allParents[i]->atomicNumber != rhs.allParents[i]->atomicNumber) {
return false;
}
if ((*scores)[allParents[i]] != (*rhs.scores)[rhs.allParents[i]]) {
return false;
}
}
if (theseAtoms.size() != rhs.theseAtoms.size()) {
return false;
}
for (size_t i = 0; i < theseAtoms.size(); i++) {
if (theseAtoms[i].first != rhs.theseAtoms[i].first) {
return false;
}
}
return true;
}
std::ostream& operator<<(std::ostream& os, const CIPAtom& a)
{
for (size_t i = 0; i < a.allParents.size(); i++) {
os << a.allParents[i]->atomicNumber << "("
<< (*a.scores)[a.allParents[i]] << ")";
if ((*a.medals)[a.allParents[i]].size()) {
cerr << "<";
for (int ii : (*a.medals)[a.allParents[i]]) {
cerr << ii << "|";
}
cerr << ">";
}
cerr << " ";
}
os << "-";
for (const auto& theseAtom : a.theseAtoms) {
os << " " << theseAtom.first;
}
return os;
}
bool CIPAtom::isBetter(CIPAtom& rhs,
map<sketcherMinimizerAtom*, unsigned int>* m) const
{
assert(allParents.size() == rhs.allParents.size());
for (size_t i = 0; i < allParents.size(); i++) {
if ((*m)[allParents[i]] > (*m)[rhs.allParents[i]]) {
return true;
}
if ((*m)[allParents[i]] < (*m)[rhs.allParents[i]]) {
return false;
}
if (allParents[i]->atomicNumber > rhs.allParents[i]->atomicNumber) {
return true;
}
if (allParents[i]->atomicNumber < rhs.allParents[i]->atomicNumber) {
return false;
}
if ((*scores)[allParents[i]] < (*rhs.scores)[rhs.allParents[i]]) {
return true;
}
if ((*scores)[allParents[i]] > (*rhs.scores)[rhs.allParents[i]]) {
return false;
}
vector<int> meds = (*medals)[allParents[i]];
vector<int> meds2 = (*rhs.medals)[rhs.allParents[i]];
size_t s = (meds.size() < meds2.size()) ? meds.size() : meds2.size();
for (size_t mm = 0; mm < s; mm++) {
if (meds[mm] > meds2[mm]) {
return true;
}
if (meds[mm] < meds2[mm]) {
return false;
}
}
if (meds.size() > meds2.size()) {
return true;
}
if (meds2.size() > meds.size()) {
return false;
}
}
size_t siz = theseAtoms.size();
if (rhs.theseAtoms.size() < siz) {
siz = rhs.theseAtoms.size();
}
for (size_t i = 0; i < siz; i++) {
if (theseAtoms[i].first > rhs.theseAtoms[i].first) {
return true;
}
if (theseAtoms[i].first < rhs.theseAtoms[i].first) {
return false;
}
}
if (theseAtoms.size() > rhs.theseAtoms.size()) {
return true;
}
if (theseAtoms.size() < rhs.theseAtoms.size()) {
return false;
}
return false;
}
sketcherMinimizerAtom::~sketcherMinimizerAtom() = default;
sketcherMinimizerAtom::sketcherMinimizerAtom()
: crossLayout(false), fixed(false), constrained(false), rigid(false),
isSharedAndInner(false), atomicNumber(6), charge(0), _valence(-10),
_generalUseN(-1), _generalUseN2(-1), m_chmN(-1),
_generalUseVisited(false), _generalUseVisited2(false), fragment(nullptr),
needsCheckForClashes(false), visited(false), coordinatesSet(false),
isR(true), hasStereochemistrySet(false), _hasRingChirality(false)
{
hidden = false;
m_pseudoZ = 0.f;
m_pocketDistance = 0.f;
m_x3D = INVALID_COORDINATES;
m_y3D = INVALID_COORDINATES;
m_z3D = INVALID_COORDINATES;
m_isClashing = false;
m_isLigand = false;
m_isWaterMap = false;
m_clockwiseInvert = false;
m_isStereogenic = false;
m_ignoreRingChirality = false;
}
sketcherMinimizerRing*
sketcherMinimizerAtom::shareARing(const sketcherMinimizerAtom* atom1,
const sketcherMinimizerAtom* atom2)
{
if (!atom1->rings.size()) {
return nullptr;
}
if (!atom2->rings.size()) {
return nullptr;
}
for (sketcherMinimizerRing* ring : atom1->rings) {
if (ring->isMacrocycle()) {
continue;
}
for (sketcherMinimizerRing* ring2 : atom2->rings) {
if (ring == ring2) {
return ring;
}
}
}
for (sketcherMinimizerRing* ring : atom1->rings) {
for (sketcherMinimizerRing* ring2 : atom2->rings) {
if (ring == ring2) {
return ring;
}
}
}
return nullptr;
}
int sketcherMinimizerAtom::findHsNumber() const
{
int valence = _valence;
if (valence == -10) {
valence = expectedValence(atomicNumber); }
int nBondOrders = 0;
for (auto bond : bonds) {
nBondOrders += bond->bondOrder;
}
if (atomicNumber == 16) { int nOs = 0;
for (size_t i = 0; i < neighbors.size(); ++i) {
if (neighbors[i]->atomicNumber == 8 && bonds[i]->bondOrder == 2) {
++nOs;
}
}
if ((nOs) < 3) {
valence += nOs * 2;
}
}
if (atomicNumber == 15) { int nOs = 0;
for (size_t i = 0; i < neighbors.size(); ++i) {
if (neighbors[i]->atomicNumber == 8 && bonds[i]->bondOrder == 2) {
++nOs;
}
}
if (nOs < 2) {
valence += nOs * 2;
}
}
int out = valence - nBondOrders + charge;
if (out < 0) {
out = 0;
} else if (out > 4) {
out = 4;
}
return out;
}
unsigned int
sketcherMinimizerAtom::expectedValence(unsigned int atomicNumber) const
{
switch (atomicNumber) {
case 1:
return 1;
case 5:
return 3;
case 6:
return 4;
case 7:
return 3;
case 8:
return 2;
case 9:
return 1;
case 14:
return 4;
case 15:
return 3;
case 16:
return 2;
case 17:
return 1;
case 34:
return 2;
case 35:
return 1;
case 53:
return 1;
default:
return 4;
}
return 4;
}
vector<sketcherMinimizerAtom*>
sketcherMinimizerAtom::clockwiseOrderedNeighbors() const
{
vector<sketcherMinimizerAtom*> orderedNeighs;
vector<sketcherMinimizerAtom*> neighs = neighbors;
int lastPoppedIndex = 0;
sketcherMinimizerAtom* lastPoppedAtom = neighs[lastPoppedIndex];
orderedNeighs.push_back(lastPoppedAtom);
neighs.erase(neighs.begin() + lastPoppedIndex);
while (neighs.size()) {
float smallestAngle = 361;
for (unsigned int i = 0; i < neighs.size(); i++) {
float newAngle = sketcherMinimizerMaths::signedAngle(
lastPoppedAtom->coordinates, coordinates,
neighs[i]->coordinates);
if (newAngle < 0) {
newAngle += 360;
}
if (newAngle < smallestAngle) {
smallestAngle = newAngle;
lastPoppedIndex = i;
}
}
lastPoppedAtom = neighs[lastPoppedIndex];
orderedNeighs.push_back(lastPoppedAtom);
neighs.erase(neighs.begin() + lastPoppedIndex);
}
return orderedNeighs;
}
void sketcherMinimizerAtom::writeStereoChemistry() {
assert(neighbors.size() == bonds.size());
if (!hasStereochemistrySet) {
return;
}
if (!canBeChiral()) {
hasStereochemistrySet = false;
return;
} else {
sketcherMinimizerAtom dummyH;
dummyH.atomicNumber = 1;
dummyH.molecule = molecule;
sketcherMinimizerAtom dummyLP;
dummyLP.atomicNumber = 0;
dummyLP.molecule = molecule;
sketcherMinimizerAtom* dummy = &dummyH;
vector<sketcherMinimizerAtom*> neighs = neighbors;
vector<sketcherMinimizerAtom*> orderedNeighs;
vector<sketcherMinimizerBond*> bbonds = bonds;
vector<sketcherMinimizerBond*> orderedBonds;
vector<float> angles;
int lastPoppedIndex = 0;
sketcherMinimizerAtom* lastPoppedAtom = neighs[lastPoppedIndex];
orderedNeighs.push_back(lastPoppedAtom);
neighs.erase(neighs.begin() + lastPoppedIndex);
orderedBonds.push_back(bbonds[lastPoppedIndex]);
bbonds.erase(bbonds.begin() + lastPoppedIndex);
while (neighs.size()) { float smallestAngle = 361;
for (unsigned int i = 0; i < neighs.size(); i++) {
float newAngle = sketcherMinimizerMaths::signedAngle(
sketcherMinimizerPointF(lastPoppedAtom->coordinates.x(),
lastPoppedAtom->coordinates.y()),
sketcherMinimizerPointF(coordinates.x(), coordinates.y()),
sketcherMinimizerPointF(neighs[i]->coordinates.x(),
neighs[i]->coordinates.y()));
if (newAngle < 0) {
newAngle += 360;
}
if (newAngle < smallestAngle) {
smallestAngle = newAngle;
lastPoppedIndex = i;
}
}
angles.push_back(smallestAngle);
lastPoppedAtom = neighs[lastPoppedIndex];
orderedNeighs.push_back(lastPoppedAtom);
neighs.erase(neighs.begin() + lastPoppedIndex);
orderedBonds.push_back(bbonds[lastPoppedIndex]);
bbonds.erase(bbonds.begin() + lastPoppedIndex);
}
if ((atomicNumber == 7 || atomicNumber == 16) && _implicitHs == 0 &&
orderedBonds.size() == 3) {
dummy = &dummyLP;
}
bool four = true;
float totalAngle = std::accumulate(angles.begin(), angles.end(), 0.f);
angles.push_back(360.f - totalAngle);
vector<sketcherMinimizerAtomPriority> atomPriorities,
orderedAtomPriorities;
for (auto& orderedNeigh : orderedNeighs) {
sketcherMinimizerAtomPriority p;
p.a = orderedNeigh;
atomPriorities.push_back(p);
}
if (atomPriorities.size() == 3) {
four = false;
sketcherMinimizerAtomPriority p;
p.a = dummy;
atomPriorities.push_back(p);
}
bool isStereocenter = setCIPPriorities(atomPriorities, this);
if (!isStereocenter) {
if (!m_ignoreRingChirality) {
_hasRingChirality = true;
isStereocenter = setCIPPriorities(atomPriorities, this);
}
}
if (!isStereocenter) {
_hasRingChirality = false;
}
orderedAtomPriorities = atomPriorities;
orderAtomPriorities(orderedAtomPriorities, this);
if (isStereocenter) {
if (!four) {
if (orderedAtomPriorities[0].a == dummy) {
orderedAtomPriorities.push_back(orderedAtomPriorities[0]);
orderedAtomPriorities.insert(orderedAtomPriorities.begin(),
orderedAtomPriorities[3]);
orderedAtomPriorities.erase(orderedAtomPriorities.begin() +
1);
orderedAtomPriorities.erase(orderedAtomPriorities.begin() +
3);
}
}
unsigned int startIndex = 0;
for (unsigned int i = 0; i < atomPriorities.size(); i++) {
if (atomPriorities[i].a == orderedAtomPriorities[0].a) {
startIndex = i;
break;
}
}
for (unsigned int i = 0; i < startIndex; i++) {
atomPriorities.push_back(atomPriorities[0]);
atomPriorities.erase(atomPriorities.begin());
}
if (four) {
if (atomPriorities[1].a == orderedAtomPriorities[3].a) {
atomPriorities.push_back(atomPriorities[0]);
atomPriorities.erase(atomPriorities.begin());
}
}
sketcherMinimizerAtom* mainAtom = nullptr;
if (four) {
if (atomPriorities[0].a == orderedAtomPriorities[0].a) {
mainAtom = atomPriorities[0].a;
}
if (atomPriorities[3].a == orderedAtomPriorities[0].a) {
mainAtom = atomPriorities[3].a;
}
}
bool invert = false;
sketcherMinimizerBond* b1 = bondTo(atomPriorities[0].a);
if (b1) {
if (!four || mainAtom == atomPriorities[0].a ||
(!sketcherMinimizer::sameRing(this, atomPriorities[0].a) &&
!atomPriorities[0].a->hasStereochemistrySet)) {
bool reverse = false;
if (b1->startAtom != this) {
reverse = true;
}
b1->isWedge = !invert;
b1->hasStereochemistryDisplay = true;
b1->isReversed = reverse;
} else {
b1->hasStereochemistryDisplay = false;
}
}
if (four) {
sketcherMinimizerBond* b2 = bondTo(atomPriorities[3].a);
if (b2) {
if (mainAtom == atomPriorities[3].a ||
(!sketcherMinimizer::sameRing(this,
atomPriorities[3].a) &&
!atomPriorities[3].a->hasStereochemistrySet)) {
bool reverse = false;
if (b2->startAtom != this) {
reverse = true;
}
b2->isWedge = invert;
b2->hasStereochemistryDisplay = true;
b2->isReversed = reverse;
} else {
b2->hasStereochemistryDisplay = false;
}
}
}
sketcherMinimizerBond* b3 = bondTo(atomPriorities[1].a);
if (b3) {
b3->hasStereochemistryDisplay = false;
}
sketcherMinimizerBond* b4 = bondTo(atomPriorities[2].a);
if (b4) {
b4->hasStereochemistryDisplay = false;
}
int readS = readStereochemistry(true);
readS = -readS;
if ((readS == -1 && isR) || (readS == 1 && !isR)) { invert = true;
if (b1) {
if (!four || mainAtom == atomPriorities[0].a ||
(!sketcherMinimizer::sameRing(this,
atomPriorities[0].a) &&
!atomPriorities[0].a->hasStereochemistrySet)) {
bool reverse = false;
if (b1->startAtom != this) {
reverse = true;
}
b1->isWedge = !invert;
b1->hasStereochemistryDisplay = true;
b1->isReversed = reverse;
} else {
b1->hasStereochemistryDisplay = false;
}
}
if (four) {
sketcherMinimizerBond* b2 = bondTo(atomPriorities[3].a);
if (b2) {
if (mainAtom == atomPriorities[3].a ||
(!sketcherMinimizer::sameRing(
this, atomPriorities[3].a) &&
!atomPriorities[3].a->hasStereochemistrySet)) {
bool reverse = false;
if (b2->startAtom != this) {
reverse = true;
}
b2->isWedge = invert;
b2->hasStereochemistryDisplay = true;
b2->isReversed = reverse;
} else {
b2->hasStereochemistryDisplay = false;
}
}
}
}
}
else {
for (auto b : bonds) {
b->hasStereochemistryDisplay = false;
}
}
}
}
sketcherMinimizerAtomChiralityInfo::sketcherMinimizerChirality
sketcherMinimizerAtom::getRelativeStereo(sketcherMinimizerAtom* lookingFrom,
sketcherMinimizerAtom* atom1,
sketcherMinimizerAtom* atom2)
{
readStereochemistry(); auto RSpriorities = m_RSPriorities;
if (RSpriorities.size() < 3) {
return sketcherMinimizerAtomChiralityInfo::unspecified;
}
vector<int> priorities(4, 3);
for (unsigned int nn = 0; nn < neighbors.size(); nn++) {
sketcherMinimizerAtom* n = neighbors[nn];
if (n == atom1) {
priorities[0] = RSpriorities[nn];
} else if (n == atom2) {
priorities[1] = RSpriorities[nn];
} else if (n == lookingFrom) {
priorities[3] = RSpriorities[nn];
} else {
priorities[2] = RSpriorities[nn];
}
}
vector<int> can(4);
for (unsigned int i = 0; i < 4; i++) {
can[i] = i;
}
bool match = sketcherMinimizerAtom::matchCIPSequence(priorities, can);
bool isClockWise = (match ? !isR : isR);
if (isClockWise) {
return sketcherMinimizerAtomChiralityInfo::clockwise;
}
return sketcherMinimizerAtomChiralityInfo::counterClockwise;
}
bool sketcherMinimizerAtom::setAbsoluteStereoFromChiralityInfo()
{
auto info = m_chiralityInfo;
if (info.direction == sketcherMinimizerAtomChiralityInfo::unspecified) {
return true;
}
readStereochemistry(); auto RSpriorities = m_RSPriorities;
if (RSpriorities.size() < 3) {
cerr << "CHMMol-> sketcher stereo error: wrong number for RSpriorities"
<< endl;
return false;
}
vector<int> priorities(4, 5);
bool at3 = false;
for (unsigned int nn = 0; nn < neighbors.size(); nn++) {
sketcherMinimizerAtom* n = neighbors[nn];
if (n == info.atom1) {
priorities[0] = RSpriorities[nn];
} else if (n == info.atom2) {
priorities[1] = RSpriorities[nn];
} else if (n == info.lookingFrom) {
priorities[3] = RSpriorities[nn];
} else {
if (at3) {
cerr << "CHMMol-> sketcher stereo error: more than 1 atom not "
"matching"
<< endl;
return false;
} else {
at3 = true;
priorities[2] = RSpriorities[nn];
}
}
}
int addingHN = 0;
if (priorities[0] == 5) {
priorities[0] = 3;
addingHN++;
}
if (priorities[1] == 5) {
priorities[1] = 3;
addingHN++;
}
if (priorities[2] == 5) {
priorities[2] = 3;
addingHN++;
}
if (priorities[3] == 5) {
priorities[3] = 3;
addingHN++;
}
if (addingHN > 1) {
cerr << "CHMMol-> sketcher stereo error: more than 1 H on chiral center"
<< endl;
return false;
}
bool invert = false;
vector<int> can(4);
for (unsigned int i = 0; i < 4; i++) {
can[i] = i;
}
if (!sketcherMinimizerAtom::matchCIPSequence(priorities, can)) {
invert = !invert;
}
bool isRBool = true;
if (info.direction == sketcherMinimizerAtomChiralityInfo::clockwise) {
isRBool = false;
}
if (invert) {
isRBool = !isRBool;
}
isR = isRBool;
hasStereochemistrySet = true;
return true;
}
bool sketcherMinimizerAtom::matchCIPSequence(vector<int>& v1, vector<int>& v2)
{
if (v1.size() < v2.size()) {
v1.push_back(3);
} else if (v2.size() < v1.size()) {
v2.push_back(3);
}
int outofPlaceNs = 0;
for (unsigned int i = 0; i < v1.size(); i++) {
if (v1[i] != v2[i]) {
outofPlaceNs++;
}
}
if (outofPlaceNs == 2) {
return false;
} else if (outofPlaceNs == 4) {
int n1 = v1[0];
int index2 = 0;
for (unsigned int j = 0; j < v2.size(); j++) {
if (v2[j] == n1) {
index2 = j;
break;
}
}
if (v1[index2] != v2[0]) {
return false;
}
}
return true;
}
void sketcherMinimizerAtom::setCoordinates(sketcherMinimizerPointF coords)
{
coordinates = std::move(coords);
coordinates.round();
coordinatesSet = true;
}
bool sketcherMinimizerAtom::hasNoStereoActiveBonds() const
{
for (auto bond : bonds) {
if (bond->isStereo()) {
return false;
}
}
return true;
}
void sketcherMinimizerAtom::orderAtomPriorities(
vector<sketcherMinimizerAtomPriority>& atomPriorities,
sketcherMinimizerAtom* center) {
assert(atomPriorities.size() == 4);
vector<float> weights(4);
for (unsigned int i = 0; i < 4; i++) {
queue<sketcherMinimizerAtom*> q;
for (auto& _atom : center->molecule->_atoms) {
_atom->_generalUseVisited = false;
}
q.push(atomPriorities[i].a);
center->_generalUseVisited = true;
atomPriorities[i].a->_generalUseVisited = true;
int counter = 0;
while (q.size()) {
counter++;
sketcherMinimizerAtom* at = q.front();
q.pop();
for (auto n : at->neighbors) {
if (!n->_generalUseVisited) {
q.push(n);
n->_generalUseVisited = true;
}
}
}
weights[i] = static_cast<float>(counter);
sketcherMinimizerBond* b = center->bondTo(atomPriorities[i].a);
if (b) {
if (b->bondOrder == 2) {
weights[i] -=
0.25; }
if (center->atomicNumber == 16 && b->bondOrder == 2) {
weights[i] += 2000; }
if (sketcherMinimizer::sameRing(b->startAtom, b->endAtom)) {
weights[i] +=
500; }
}
if (atomPriorities[i].a->atomicNumber == 6) {
weights[i] += 0.5; }
if (atomPriorities[i].a->atomicNumber == 1) {
weights[i] -= 0.5;
}
if (atomPriorities[i].a->isSharedAndInner &&
!center->isSharedAndInner) {
weights[i] -= 2000; }
if (center->crossLayout) {
if (atomPriorities[i].a->neighbors.size() > 1) {
weights[i] += 200;
}
}
if ( atomPriorities[i]
.a->hasStereochemistrySet) {
weights[i] += 10000; }
for (unsigned int j = 0; j < atomPriorities[i].a->bonds.size(); j++) {
if (atomPriorities[i].a->bonds[j]->bondOrder == 2) {
weights[i] += 100;
break;
}
}
}
float lowestWeight = weights[0];
int index = 0;
sketcherMinimizerAtomPriority firstAtom = atomPriorities[0];
for (unsigned int i = 1; i < 4; i++) {
if (weights[i] < lowestWeight) {
lowestWeight = weights[i];
index = i;
}
}
firstAtom = atomPriorities[index];
atomPriorities.erase(atomPriorities.begin() + index);
weights.erase(weights.begin() + index);
index = 0;
lowestWeight = weights[0];
sketcherMinimizerAtomPriority secondAtom = atomPriorities[0];
for (unsigned int i = 1; i < 3; i++) {
if (weights[i] < lowestWeight) {
lowestWeight = weights[i];
index = i;
}
}
secondAtom = atomPriorities[index];
atomPriorities.erase(atomPriorities.begin() + index);
if ((center->atomicNumber != 16 && center->atomicNumber != 15) ||
center->neighbors.size() != 4) {
atomPriorities.push_back(secondAtom);
atomPriorities.insert(atomPriorities.begin(), firstAtom);
} else {
atomPriorities.insert(atomPriorities.begin() + 1, secondAtom);
atomPriorities.insert(atomPriorities.begin(), firstAtom);
}
}
bool sketcherMinimizerAtom::setCIPPriorities(
vector<sketcherMinimizerAtomPriority>& atomPriorities,
sketcherMinimizerAtom* center)
{
for (auto& atomPrioritie : atomPriorities) {
atomPrioritie.priority = 3;
}
if (atomPriorities.size() != 4) {
return false;
}
for (unsigned int i = 0; i < atomPriorities.size() - 1; i++) {
for (unsigned int j = i + 1; j < atomPriorities.size(); j++) {
sketcherMinimizerAtom* res =
CIPPriority(atomPriorities[i].a, atomPriorities[j].a, center);
if (res == atomPriorities[i].a) {
atomPriorities[i].priority--;
} else if (res == atomPriorities[j].a) {
atomPriorities[j].priority--;
}
}
}
vector<bool> found(4, false);
for (auto& atomPrioritie : atomPriorities) {
if (found[atomPrioritie.priority]) {
return false; }
found[atomPrioritie.priority] = true;
}
return true;
}
sketcherMinimizerAtom*
sketcherMinimizerAtom::CIPPriority(sketcherMinimizerAtom* at1,
sketcherMinimizerAtom* at2,
sketcherMinimizerAtom* center)
{
assert(center->molecule);
assert(at1->molecule == center->molecule);
assert(at2->molecule == center->molecule);
assert(center->molecule->_atoms.size());
assert(at1);
assert(at2);
if (at1->atomicNumber > at2->atomicNumber) {
return at1;
} else if (at2->atomicNumber > at1->atomicNumber) {
return at2;
}
if (center->_hasRingChirality && !center->m_ignoreRingChirality) {
if (sketcherMinimizer::sameRing(center, at1, at2)) {
if (at1->_generalUseN > at2->_generalUseN) {
return at1;
} else {
return at2;
}
}
}
vector<CIPAtom> AN1, AN2;
map<sketcherMinimizerAtom *, int> score1,
score2; map<sketcherMinimizerAtom *, vector<int>> medals1,
medals2; map<sketcherMinimizerAtom *, int> visited1,
visited2;
visited1[center] = 1;
visited2[center] = 1;
visited1[at1] = 2;
visited2[at2] = 2;
vector<pair<int, sketcherMinimizerAtom *>> v1, v2;
v1.emplace_back(at1->atomicNumber, at1);
v2.emplace_back(at2->atomicNumber, at2);
vector<sketcherMinimizerAtom *> parents1, parents2;
parents1.push_back(center);
parents1.push_back(at1);
parents2.push_back(center);
parents2.push_back(at2);
AN1.emplace_back(v1, center, parents1, &score1, &medals1, &visited1);
AN2.emplace_back(v2, center, parents2, &score2, &medals2, &visited2);
int level = 1;
while (AN1.size() || AN2.size()) {
level++;
stable_sort(AN1.begin(), AN1.end());
stable_sort(AN2.begin(), AN2.end());
sketcherMinimizerAtom::assignMedals(AN1);
sketcherMinimizerAtom::assignMedals(AN2);
sketcherMinimizerAtom::chooseFirstAndSortAccordingly(AN1);
sketcherMinimizerAtom::chooseFirstAndSortAccordingly(AN2);
sketcherMinimizerAtom::finalizeScores(AN1);
sketcherMinimizerAtom::finalizeScores(AN2);
size_t nn = AN1.size();
if (AN2.size() < nn) {
nn = AN2.size();
}
for (size_t i = 0; i < nn; ++i) {
if (AN1[i] < AN2[i]) {
return at1;
}
if (AN2[i] < AN1[i]) {
return at2;
}
}
if (AN1.size() > AN2.size()) {
return at1;
}
if (AN2.size() > AN1.size()) {
return at2;
}
AN1 = sketcherMinimizerAtom::expandOneLevel(AN1);
AN2 = sketcherMinimizerAtom::expandOneLevel(AN2);
}
return nullptr;
}
void sketcherMinimizerAtom::chooseFirstAndSortAccordingly(vector<CIPAtom>& V)
{
if (V.size() < 2) {
return;
}
vector<CIPAtom> copyV = V;
V.clear();
map<sketcherMinimizerAtom*, unsigned int> friendsMask;
while (copyV.size()) {
int bestI = 0;
for (unsigned int i = 1; i < copyV.size(); i++) {
if (copyV[i].isBetter(copyV[bestI], &friendsMask)) {
bestI = i;
}
}
CIPAtom newBest = copyV[bestI];
copyV.erase(copyV.begin() + bestI);
V.push_back(newBest);
for (auto allParent : newBest.allParents) {
friendsMask[allParent] |= (1 << copyV.size());
}
}
}
vector<CIPAtom> sketcherMinimizerAtom::expandOneLevel(vector<CIPAtom>& oldV)
{
map<sketcherMinimizerAtom*, bool> visitedThisRound;
vector<CIPAtom> newV;
for (auto& an : oldV) {
for (unsigned int aa = 0; aa < an.theseAtoms.size(); aa++) {
sketcherMinimizerAtom* a = an.theseAtoms[aa].second;
if (a == nullptr) {
continue; }
visitedThisRound[a] = true;
map<sketcherMinimizerAtom*, int>* visited = an.visited;
map<sketcherMinimizerAtom*, vector<int>>* medals = an.medals;
map<sketcherMinimizerAtom*, int>* scores = an.scores;
vector<sketcherMinimizerAtom*> allParents = an.allParents;
allParents.push_back(a);
vector<pair<int, sketcherMinimizerAtom*>> theseAts;
for (unsigned int n = 0; n < a->bonds.size(); n++) {
sketcherMinimizerAtom* neigh = a->neighbors[n];
if (neigh != an.parent) { bool ghost =
(neigh->atomicNumber == 1 ||
((*visited)[neigh] &&
(*visited)[neigh] !=
(*visited)[a] + 1) );
theseAts.emplace_back(
neigh->atomicNumber,
ghost ? ((sketcherMinimizerAtom*) nullptr)
: neigh); if (!ghost) {
(*visited)[neigh] = (*visited)[a] + 1;
}
}
if (a->bonds[n]->bondOrder == 2) { theseAts.emplace_back(neigh->atomicNumber,
(sketcherMinimizerAtom*) nullptr);
}
else if (a->bonds[n]->bondOrder == 3) {
theseAts.emplace_back(neigh->atomicNumber,
(sketcherMinimizerAtom*) nullptr);
theseAts.emplace_back(neigh->atomicNumber,
(sketcherMinimizerAtom*) nullptr);
}
}
for (int counter = 0; counter < a->_implicitHs;
counter++) { theseAts.emplace_back(1, (sketcherMinimizerAtom*) nullptr);
}
stable_sort(theseAts.begin(), theseAts.end());
reverse(theseAts.begin(), theseAts.end());
newV.emplace_back(theseAts, a, allParents, scores, medals, visited);
}
}
return newV;
}
void sketcherMinimizerAtom::assignMedals(vector<CIPAtom>& v)
{
if (v.size() < 1) {
return;
}
map<sketcherMinimizerAtom*, vector<int>>* medals = v[0].medals;
vector<bool> isEqualToPrevious(v.size());
for (unsigned int i = 1; i < v.size();
i++) {
isEqualToPrevious[i] = (v[i] == v[i - 1]);
}
unsigned int medalLvl = 0;
for (unsigned int i = 0; i < v.size(); i++) {
if (i > 0) {
if (isEqualToPrevious[i]) {
assert(medalLvl > 0);
medalLvl--;
}
}
for (auto allParent : v[i].allParents) {
vector<int> medalsV = (*medals)[allParent];
while (medalsV.size() < medalLvl) {
medalsV.push_back(0);
}
if (medalsV.size() > medalLvl) {
medalsV[medalLvl] = medalsV[medalLvl] + 1;
} else {
medalsV.push_back(1);
}
(*medals)[allParent] = medalsV;
}
medalLvl++;
}
}
void sketcherMinimizerAtom::finalizeScores(vector<CIPAtom>& v)
{
if (v.size() < 1) {
return;
}
vector<bool> isEqualToPrevious(v.size());
for (unsigned int i = 1; i < v.size();
i++) {
isEqualToPrevious[i] = (v[i] == v[i - 1]);
}
map<sketcherMinimizerAtom*, vector<int>>* medals = v[0].medals;
map<sketcherMinimizerAtom*, int>* scores = v[0].scores;
scores->clear();
int score = 1;
for (unsigned int i = 0; i < v.size(); i++) {
if (i > 0) {
if (isEqualToPrevious[i]) {
score--;
}
}
for (auto allParent : v[i].allParents) {
if ((*scores)[allParent] == 0) {
(*scores)[allParent] = score;
}
}
score++;
}
medals->clear();
}
sketcherMinimizerBond*
sketcherMinimizerAtom::bondTo(sketcherMinimizerAtom* at) const
{
for (unsigned int i = 0; i < neighbors.size(); i++) {
if (neighbors[i] == at) {
return bonds[i];
}
}
return nullptr;
}
bool sketcherMinimizerAtom::isResidue() const
{
return false;
}
int sketcherMinimizerAtom::readStereochemistry(
bool readOnly) {
if (!readOnly) {
_hasRingChirality = false;
m_isStereogenic = false;
}
if (!canBeChiral()) {
return 0;
}
sketcherMinimizerAtom dummyH;
dummyH.atomicNumber = 1;
dummyH.molecule = molecule;
sketcherMinimizerAtom dummyLP;
dummyLP.atomicNumber = 0;
dummyLP.molecule = molecule;
vector<sketcherMinimizerAtom*> neighs = neighbors;
vector<sketcherMinimizerAtom*> orderedNeighs;
vector<sketcherMinimizerBond*> bnds = bonds;
vector<sketcherMinimizerBond*> orderedBonds;
vector<float> angles;
int lastPoppedIndex = 0;
sketcherMinimizerAtom* lastPoppedAtom = neighs[lastPoppedIndex];
orderedNeighs.push_back(lastPoppedAtom);
neighs.erase(neighs.begin() + lastPoppedIndex);
orderedBonds.push_back(bnds[lastPoppedIndex]);
bnds.erase(bnds.begin() + lastPoppedIndex);
while (neighs.size()) { float smallestAngle = 361;
for (unsigned int i = 0; i < neighs.size(); i++) {
float newAngle = sketcherMinimizerMaths::signedAngle(
lastPoppedAtom->coordinates, coordinates,
neighs[i]->coordinates);
if (newAngle < 0) {
newAngle += 360;
}
if (newAngle < smallestAngle) {
smallestAngle = newAngle;
lastPoppedIndex = i;
}
}
angles.push_back(smallestAngle);
lastPoppedAtom = neighs[lastPoppedIndex];
orderedNeighs.push_back(lastPoppedAtom);
neighs.erase(neighs.begin() + lastPoppedIndex);
orderedBonds.push_back(bnds[lastPoppedIndex]);
bnds.erase(bnds.begin() + lastPoppedIndex);
}
float totalAngle = 0;
for (float angle : angles) {
totalAngle += angle;
}
angles.push_back(360.f - totalAngle);
bool semiplane = false;
sketcherMinimizerAtom* centralAtom = nullptr;
if (angles.size() == 3) {
for (unsigned int i = 0; i < angles.size(); i++) {
if (angles[i] > 180.f) {
semiplane = true;
size_t precI;
if (i == 0) {
precI = angles.size() - 1;
} else {
precI = i - 1;
}
centralAtom = orderedNeighs[precI];
}
}
}
int wedgeN = -1;
int dashedN = -1;
bool giveUp = false;
assert(orderedBonds.size() == 4 || orderedBonds.size() == 3);
for (unsigned int i = 0; i < orderedBonds.size(); i++) {
if (orderedBonds[i]->hasStereochemistryDisplay) {
if (!orderedBonds[i]->isReversed &&
orderedBonds[i]->startAtom != this) {
continue;
}
if (orderedBonds[i]->isReversed &&
orderedBonds[i]->endAtom != this) {
continue;
}
bool wedge = orderedBonds[i]->isWedge;
if (orderedBonds[i]->isReversed) {
wedge = !wedge;
}
if (orderedBonds[i]->startAtom != this) {
wedge = !wedge;
}
if (wedge) {
if (wedgeN == -1) {
wedgeN = i;
} else {
giveUp = true;
}
} else {
if (dashedN == -1) {
dashedN = i;
} else {
giveUp = true;
}
}
}
}
int startIndex = 0;
bool invert = false;
if (dashedN == -1 && wedgeN == -1) {
giveUp = true;
} else if (dashedN == -1) {
startIndex = wedgeN;
} else if (wedgeN == -1) {
startIndex = dashedN;
invert = true;
} else {
if (orderedBonds.size() == 3) {
return 0;
}
if (wedgeN - dashedN == 1 || wedgeN - dashedN == -3) {
startIndex = wedgeN;
} else if (wedgeN - dashedN == -1 || wedgeN - dashedN == 3) {
startIndex = dashedN;
invert = true;
}
else {
giveUp = true;
}
}
int nOfHs = _implicitHs;
size_t totalSubstituentsN = neighbors.size() + nOfHs;
if (orderedNeighs.size() == 3 && nOfHs == 1) {
if (semiplane) {
if (centralAtom == orderedNeighs[startIndex]) {
invert = !invert;
}
}
orderedNeighs.insert(orderedNeighs.begin() + startIndex, &dummyH);
invert = !invert;
}
if (orderedNeighs.size() == 3 &&
(atomicNumber == 7 ||
atomicNumber == 16)) {
orderedNeighs.insert(orderedNeighs.begin() + startIndex, &dummyLP);
invert = !invert;
}
vector<sketcherMinimizerAtomPriority> atomPriorities;
for (auto& orderedNeigh : orderedNeighs) {
sketcherMinimizerAtomPriority p;
p.a = orderedNeigh;
atomPriorities.push_back(p);
}
if (atomPriorities.size() != 4) {
return 0;
}
vector<int> canonical;
canonical.push_back(0);
canonical.push_back(1);
canonical.push_back(2);
canonical.push_back(3);
m_RSPriorities.clear();
bool isStereocenter = setCIPPriorities(atomPriorities, this);
if (!isStereocenter) {
if (!readOnly) {
if (!m_ignoreRingChirality) {
_hasRingChirality = true;
isStereocenter = setCIPPriorities(atomPriorities, this);
}
}
}
if (!isStereocenter) {
if (!readOnly) {
_hasRingChirality = false;
}
}
if (!isStereocenter) {
giveUp = true;
} else {
if (!readOnly) {
m_isStereogenic = true;
}
}
if (totalSubstituentsN < 4 && (atomicNumber != 7 && atomicNumber != 16)) {
if (!readOnly) {
m_isStereogenic = false;
}
}
if (!m_isStereogenic) {
if (!readOnly) {
_hasRingChirality = false;
}
}
for (auto n : neighbors) {
for (auto& atomPrioritie : atomPriorities) {
if (atomPrioritie.a == n) {
m_RSPriorities.push_back(atomPrioritie.priority);
break;
}
}
}
if (!matchCIPSequence(canonical, m_RSPriorities)) {
m_clockwiseInvert = true;
} else {
m_clockwiseInvert = false;
}
if (!giveUp) {
int outofPlaceAtoms = 0;
for (unsigned int i = 0; i < atomPriorities.size(); i++) {
int n = startIndex + i;
if (n > 3) {
n -= 4;
}
if (atomPriorities[n].priority != i) {
outofPlaceAtoms++;
}
}
if (outofPlaceAtoms == 2) {
invert = !invert;
} else if (outofPlaceAtoms == 4) {
int n2 = atomPriorities[startIndex].priority + startIndex;
if (n2 > 3) {
n2 -= 4;
}
if (atomPriorities[n2].priority != 0) {
invert = !invert; }
}
if (invert) {
return -1;
} else {
return 1;
}
}
return 0;
}
bool sketcherMinimizerAtom::canBeChiral() const
{
if (atomicNumber == 16) {
if (neighbors.size() == 3) {
return true;
}
}
if (atomicNumber == 7) {
if (neighbors.size() == 3 || neighbors.size() == 4) {
return true;
}
}
if (neighbors.size() != 3 && neighbors.size() != 4) {
return false;
}
if ((neighbors.size() + _implicitHs != 4)) {
return false;
}
return true;
}
sketcherMinimizerPointF sketcherMinimizerAtom::getSingleAdditionVector(
const vector<sketcherMinimizerAtom*>& ats)
{
sketcherMinimizerPointF out(0.f, 0.f);
int n = 0;
for (unsigned int i = 0; i < ats.size(); i++) {
sketcherMinimizerAtom* at = ats[i];
for (unsigned int j = 0; j < at->neighbors.size(); j++) {
sketcherMinimizerAtom* ne = at->neighbors[j];
if (ne->neighbors.size() > 1 &&
find(ats.begin(), ats.end(), ne) == ats.end()) {
out += ne->coordinates - at->coordinates;
n++;
}
}
}
if (n > 0) {
out /= n;
} else {
return sketcherMinimizerPointF(50, 0);
}
out *= -1;
return out;
}
sketcherMinimizerPointF sketcherMinimizerAtom::getSingleAdditionVector() const
{
sketcherMinimizerPointF out(0.f, 0.f);
float totalf = 0.f;
if (neighbors.size()) {
for (auto neighbor : neighbors) {
float f = 1.f;
if (sketcherMinimizer::sameRing(this, neighbor)) {
f = 4.f;
}
out += f * (neighbor->coordinates - coordinates);
totalf += f;
}
out /= totalf;
}
out *= -1;
return out;
}
bool sketcherMinimizerAtom::isMetal(const unsigned int atomicNumber)
{
if (atomicNumber >= 3 && atomicNumber <= 4) {
return true;
}
if (atomicNumber >= 11 && atomicNumber <= 12) {
return true;
}
if (atomicNumber >= 19 && atomicNumber <= 20) {
return true;
}
if (atomicNumber >= 37 && atomicNumber <= 38) {
return true;
}
if (atomicNumber >= 55 && atomicNumber <= 56) {
return true;
}
if (atomicNumber >= 87 && atomicNumber <= 88) {
return true;
}
if (atomicNumber >= 21 && atomicNumber <= 30) {
return true;
}
if (atomicNumber >= 39 && atomicNumber <= 48) {
return true;
}
if (atomicNumber >= 72 && atomicNumber <= 80) {
return true;
}
if (atomicNumber >= 104 && atomicNumber <= 112) {
return true;
}
if (atomicNumber >= 57 && atomicNumber <= 71) {
return true; }
if (atomicNumber >= 89 && atomicNumber <= 103) {
return true; }
if (atomicNumber == 13) {
return true;
}
if (atomicNumber == 31) {
return true;
}
if (atomicNumber == 49) {
return true;
}
if (atomicNumber == 81) {
return true;
}
if (atomicNumber == 32) {
return true;
}
if (atomicNumber == 50) {
return true;
}
if (atomicNumber == 82) {
return true;
}
if (atomicNumber == 51) {
return true;
}
if (atomicNumber == 83) {
return true;
}
if (atomicNumber == 84) {
return true;
}
return false;
}
void sketcherMinimizerAtom::mirrorCoordinates(sketcherMinimizerAtom* at,
const sketcherMinimizerBond* bond)
{
at->setCoordinates(sketcherMinimizerMaths::mirrorPoint(
at->getCoordinates(), bond->getStartAtom()->getCoordinates(),
bond->getEndAtom()->getCoordinates()));
}
bool sketcherMinimizerAtom::hasValid3DCoordinates() const
{
return (m_x3D < INVALID_COORDINATES && m_y3D < INVALID_COORDINATES &&
m_z3D < INVALID_COORDINATES);
}
vector<sketcherMinimizerAtom*>
sketcherMinimizerAtom::getSubmolecule(sketcherMinimizerAtom* excludedAtom)
{
vector<sketcherMinimizerAtom*> subMolecule;
queue<sketcherMinimizerAtom*> q;
map<sketcherMinimizerAtom*, bool> isVisited;
isVisited[excludedAtom] = true;
q.push(this);
isVisited[this] = true;
while (q.size()) {
sketcherMinimizerAtom* atom = q.front();
subMolecule.push_back(atom);
q.pop();
for (sketcherMinimizerAtom* neighbor : atom->neighbors) {
if (!isVisited[neighbor]) {
q.push(neighbor);
isVisited[neighbor] = true;
}
}
}
return subMolecule;
}