Coverage Report

Created: 2026-07-30 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/stereo/tetrahedral.cpp
Line
Count
Source
1
/**********************************************************************
2
  tetrahedral.h - OBTetrahedralStereo
3
4
  Copyright (C) 2009 by Tim Vandermeersch
5
6
  This file is part of the Open Babel project.
7
  For more information, see <http://openbabel.org/>
8
9
  This program is free software; you can redistribute it and/or modify
10
  it under the terms of the GNU General Public License as published by
11
  the Free Software Foundation; either version 2 of the License, or
12
  (at your option) any later version.
13
14
  This program is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
  GNU General Public License for more details.
18
19
  You should have received a copy of the GNU General Public License
20
  along with this program; if not, write to the Free Software
21
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
  02110-1301, USA.
23
 **********************************************************************/
24
#include <openbabel/stereo/tetrahedral.h>
25
#include <openbabel/mol.h>
26
27
namespace OpenBabel {
28
29
  //
30
  // OBTetrahedralStereo::Config struct
31
  //
32
33
  bool OBTetrahedralStereo::Config::operator==(const OBTetrahedralStereo::Config &other) const
34
0
  {
35
0
    if (center != other.center)
36
0
      return false;
37
0
    if ((refs.size() != 3) || (other.refs.size() != 3))
38
0
      return false;
39
    // return true if either is unspecified (i.e. accidental)
40
0
    if (!specified || !other.specified)
41
0
      return true;
42
43
    // Convert both Config's refs to same from, winding and view while
44
    // avoiding having an ImplicitRef in the 'from' position of either
45
0
    Config thisConfig, otherConfig;
46
0
    if (from == OBStereo::ImplicitRef) {
47
0
      thisConfig = OBTetraNonPlanarStereo::ToConfig(*this, refs[0], winding, view);
48
0
      otherConfig = OBTetraNonPlanarStereo::ToConfig(other, thisConfig.from, winding, view);
49
0
    }
50
0
    else if (other.from == OBStereo::ImplicitRef) {
51
0
      otherConfig = OBTetraNonPlanarStereo::ToConfig(other, other.refs[0], winding, view);
52
0
      thisConfig = OBTetraNonPlanarStereo::ToConfig(*this, otherConfig.from, winding, view);
53
0
    }
54
0
    else {
55
0
      thisConfig = *this;
56
0
      otherConfig = OBTetraNonPlanarStereo::ToConfig(other, thisConfig.from, winding, view);
57
0
    }
58
59
0
    if (!OBStereo::ContainsSameRefs(thisConfig.refs, otherConfig.refs)) {
60
0
      if (OBStereo::ContainsRef(thisConfig.refs, OBStereo::ImplicitRef)) {
61
        // if both refs already contain ImplicitRef, return false
62
0
        if (OBStereo::ContainsRef(otherConfig.refs, OBStereo::ImplicitRef))
63
0
          return false;
64
65
        // example: *this       = 23H
66
        //          otherConfig = 234 --> 23H
67
68
        // for each ref in otherConfig
69
0
        for (unsigned int i = 0; i < otherConfig.refs.size(); ++i) {
70
0
          bool found = false;
71
0
          for (OBStereo::RefIter j = thisConfig.refs.begin(); j != thisConfig.refs.end(); ++j)
72
0
            if (otherConfig.refs.at(i) == *j)
73
0
              found = true;
74
75
0
          if (!found) {
76
            // the ref from otherConfig is not found in this config
77
0
            otherConfig.refs[i] = OBStereo::ImplicitRef;
78
0
            break;
79
0
          }
80
0
        }
81
0
      } else
82
0
      if (OBStereo::ContainsRef(otherConfig.refs, OBStereo::ImplicitRef)) {
83
        // if both refs already contain ImplicitRef, return false
84
0
        if (OBStereo::ContainsRef(thisConfig.refs, OBStereo::ImplicitRef))
85
0
          return false;
86
87
        // example: *this       = 234
88
        //          otherConfig = 23H --> 234
89
90
        // for each ref in *this
91
0
        for (unsigned int i = 0; i < thisConfig.refs.size(); ++i) {
92
0
          bool found = false;
93
          // for each refs in otherConfig
94
0
          for (OBStereo::RefIter j = otherConfig.refs.begin(); j != otherConfig.refs.end(); ++j)
95
0
            if (thisConfig.refs.at(i) == *j)
96
0
              found = true;
97
98
0
          if (!found) {
99
0
            for (OBStereo::RefIter j = otherConfig.refs.begin(); j != otherConfig.refs.end(); ++j)
100
0
              if (*j == OBStereo::ImplicitRef)
101
0
                *j = thisConfig.refs.at(i);
102
0
            break;
103
0
          }
104
0
        }
105
0
      }
106
0
    }
107
108
0
    int Ni1 = OBStereo::NumInversions(thisConfig.refs);
109
0
    int Ni2 = OBStereo::NumInversions(otherConfig.refs);
110
0
    return ((Ni1 + Ni2) % 2 == 0);
111
0
  }
112
113
  //
114
  // OBTetrahedralStereo class
115
  //
116
117
  OBTetrahedralStereo::OBTetrahedralStereo(OBMol *mol) :
118
0
      OBTetraNonPlanarStereo(mol), m_cfg(Config())
119
0
  {
120
0
  }
121
122
  OBTetrahedralStereo::~OBTetrahedralStereo()
123
0
  {
124
0
  }
125
126
  bool OBTetrahedralStereo::IsValid() const
127
0
  {
128
0
    if (m_cfg.center == OBStereo::NoRef)
129
0
      return false;
130
0
    if (m_cfg.from == OBStereo::NoRef)
131
0
      return false;
132
0
    if (m_cfg.refs.size() != 3)
133
0
      return false;
134
0
    return true;
135
0
  }
136
137
  void OBTetrahedralStereo::SetConfig(const Config &config)
138
0
  {
139
0
    if (config.center == OBStereo::NoRef) {
140
0
      obErrorLog.ThrowError(__FUNCTION__,
141
0
          "OBTetrahedralStereo::SetConfig : center atom id is invalid.", obError);
142
0
      m_cfg = Config();
143
0
      return;
144
0
    }
145
0
    if (config.from == OBStereo::NoRef) {
146
0
      obErrorLog.ThrowError(__FUNCTION__,
147
0
          "OBTetrahedralStereo::SetConfig : from/towards atom id is invalid.", obError);
148
0
      m_cfg = Config();
149
0
      return;
150
0
    }
151
0
    if (config.refs.size() != 3) {
152
0
      std::stringstream ss;
153
0
      ss << "OBTetrahedralStereo::SetConfig : found " << config.refs.size();
154
0
      ss << " reference ids, should be 3.";
155
0
      obErrorLog.ThrowError(__FUNCTION__, ss.str(), obError);
156
0
      m_cfg = Config();
157
0
      return;
158
0
    }
159
160
0
    m_cfg = config;
161
0
  }
162
163
  OBTetrahedralStereo::Config OBTetrahedralStereo::GetConfig(
164
      OBStereo::Winding winding, OBStereo::View view) const
165
0
  {
166
0
    if (!IsValid())
167
0
      return Config();
168
169
0
    if (m_cfg.winding != OBStereo::UnknownWinding)
170
0
      return OBTetraNonPlanarStereo::ToConfig(m_cfg, m_cfg.from, winding, view);
171
0
    else
172
0
      return OBTetraNonPlanarStereo::ToConfig(m_cfg, m_cfg.from, OBStereo::UnknownWinding, view);
173
0
  }
174
175
  OBTetrahedralStereo::Config OBTetrahedralStereo::GetConfig(unsigned long from_or_towards,
176
        OBStereo::Winding winding, OBStereo::View view) const
177
0
  {
178
0
    if (!IsValid())
179
0
      return Config();
180
181
0
    if (m_cfg.winding != OBStereo::UnknownWinding)
182
0
      return OBTetraNonPlanarStereo::ToConfig(m_cfg, from_or_towards, winding, view);
183
0
    else
184
0
      return OBTetraNonPlanarStereo::ToConfig(m_cfg, from_or_towards, OBStereo::UnknownWinding, view);
185
0
  }
186
187
  bool OBTetrahedralStereo::operator==(const OBTetrahedralStereo &other) const
188
0
  {
189
0
    if (!IsValid() || !other.IsValid())
190
0
      return false;
191
192
0
    if (m_cfg == other.GetConfig())
193
0
      return true;
194
195
0
    return false;
196
0
  }
197
198
  OBGenericData* OBTetrahedralStereo::Clone(OBBase *mol) const
199
0
  {
200
0
    OBTetrahedralStereo *data = new OBTetrahedralStereo(static_cast<OBMol*>(mol));
201
0
    data->SetConfig(m_cfg);
202
0
    return data;
203
0
  }
204
205
} // namespace OpenBabel
206
207
namespace std {
208
209
  using namespace OpenBabel;
210
211
  ostream& operator<<(ostream &out, const OBTetrahedralStereo &ts)
212
0
  {
213
0
    OBTetrahedralStereo::Config cfg = ts.GetConfig();
214
0
    out << "OBTetrahedralStereo(center = " << cfg.center;
215
0
    if (cfg.view == OBStereo::ViewFrom)
216
0
      out << ", viewFrom = ";
217
0
    else
218
0
      out << ", viewTowards = ";
219
220
0
    if (cfg.from == OBStereo::ImplicitRef)
221
0
      out << "H";
222
0
    else
223
0
      out << cfg.from;
224
225
0
    out << ", refs = ";
226
0
    for (OBStereo::Refs::iterator i = cfg.refs.begin(); i != cfg.refs.end(); ++i)
227
0
      if (*i != OBStereo::ImplicitRef)
228
0
        out << *i << " ";
229
0
      else
230
0
        out << "H ";
231
232
0
    if (!cfg.specified)
233
0
      out << ", unspecified)";
234
0
    else {
235
0
      if (cfg.winding == OBStereo::Clockwise)
236
0
        out << ", clockwise)";
237
0
      else
238
0
        out << ", anti-clockwise)";
239
0
    }
240
241
0
    return out;
242
0
  }
243
244
  ostream& operator<<(ostream &out, const OBTetrahedralStereo::Config &cfg)
245
0
  {
246
0
    out << "OBTetrahedralStereo::Config(center = " << cfg.center;
247
0
    if (cfg.view == OBStereo::ViewFrom)
248
0
      out << ", viewFrom = ";
249
0
    else
250
0
      out << ", viewTowards = ";
251
252
0
    if (cfg.from == OBStereo::ImplicitRef)
253
0
      out << "H";
254
0
    else
255
0
      out << cfg.from;
256
257
0
    out << ", refs = ";
258
0
    for (OBStereo::Refs::const_iterator i = cfg.refs.begin(); i != cfg.refs.end(); ++i)
259
0
      if (*i != OBStereo::ImplicitRef)
260
0
        out << *i << " ";
261
0
      else
262
0
        out << "H ";
263
264
0
    if (!cfg.specified)
265
0
      out << ", unspecified)";
266
0
    else {
267
0
      if (cfg.winding == OBStereo::Clockwise)
268
0
        out << ", clockwise)";
269
0
      else
270
0
        out << ", anti-clockwise)";
271
0
    }
272
273
0
    return out;
274
0
  }
275
276
} // namespace std
277