Coverage Report

Created: 2026-09-04 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/stereo/squareplanar.cpp
Line
Count
Source
1
#include <openbabel/stereo/squareplanar.h>
2
#include <openbabel/mol.h>
3
#include <openbabel/oberror.h>
4
#include <algorithm> // std::rotate
5
#include <cassert>
6
7
namespace OpenBabel {
8
9
  //
10
  // OBSquarePlanarStereo::Config struct
11
  //
12
13
  bool OBSquarePlanarStereo::Config::operator==(const Config &other) const
14
0
  {
15
0
    if (center != other.center)
16
0
      return false;
17
0
    if ((refs.size() != 4) || (other.refs.size() != 4))
18
0
      return false;
19
20
0
    Config u1, u2;
21
0
    if (!OBStereo::ContainsSameRefs(refs, other.refs)) {
22
      // find a ref that occurs in both
23
0
      for (OBStereo::ConstRefIter i = refs.begin(); i != refs.end(); ++i)
24
0
        if (OBStereo::ContainsRef(other.refs, *i)) {
25
0
          u1 = OBTetraPlanarStereo::ToConfig(*this, *i, OBStereo::ShapeU); // refs[0] = u1.refs[0]
26
0
          u2 = OBTetraPlanarStereo::ToConfig(other, *i, OBStereo::ShapeU); // refs[0] = u2.refs[0]
27
0
        }
28
29
      // check if they actualy share an id...
30
0
      if (u1.refs.empty())
31
0
        return false;
32
0
    } else {
33
      // normalize the other Config struct
34
0
      u1 = OBTetraPlanarStereo::ToConfig(*this, refs.at(0), OBStereo::ShapeU); // refs[0] = u1.refs[0]
35
0
      u2 = OBTetraPlanarStereo::ToConfig(other, refs.at(0), OBStereo::ShapeU); // refs[0] = u2.refs[0]
36
      // both now start with the same ref
37
      //
38
      // 2 possiblilities:
39
      //
40
      //   1 2 3 4      1 2 3 4
41
      //   |   |        |   |      <- in any case, refs[0] & refs[2] remain unchanged
42
      //   1 2 3 4      1 4 3 2
43
      //
44
0
      return (u1.refs[2] == u2.refs[2]);
45
0
    }
46
47
    // possibilities:
48
    //
49
    //   1 2 3 4
50
    //   |   |      <- refs[0] & refs[2] remain unchanged
51
    //   1 H 3 H
52
    //
53
    //   1 2 3 4
54
    //   |     |    <- refs[0] & refs[3] remain unchanged
55
    //   1 H H 4
56
    //
57
    //   1 2 3 4
58
    //   | |        <- refs[0] & refs[1] remain unchanged
59
    //   1 2 H H
60
0
    if ((u1.refs[2] == OBStereo::ImplicitRef) || (u2.refs[2] == OBStereo::ImplicitRef)) {
61
      // 1 2 H 4
62
0
      if ((u1.refs[3] == OBStereo::ImplicitRef) || (u2.refs[3] == OBStereo::ImplicitRef)) {
63
0
        return (u1.refs[1] == u2.refs[1]); // 1 2 H H
64
0
      } else {
65
0
        return (u1.refs[3] == u2.refs[3]); // 1 H H 4
66
0
      }
67
0
    } else
68
0
      return (u1.refs[2] == u2.refs[2]); // 1 2 3 4  &  1 H 3 4  &  1 2 3 H
69
70
0
    return false;
71
0
  }
72
73
74
  //
75
  // OBSquarePlanar class
76
  //
77
78
0
  OBSquarePlanarStereo::OBSquarePlanarStereo(OBMol *mol) : OBTetraPlanarStereo(mol)
79
0
  {
80
0
  }
81
82
  OBSquarePlanarStereo::~OBSquarePlanarStereo()
83
0
  {
84
0
  }
85
86
  bool OBSquarePlanarStereo::IsValid() const
87
0
  {
88
0
    if (m_cfg.center == OBStereo::NoRef)
89
0
      return false;
90
0
    if (m_cfg.refs.size() != 4)
91
0
      return false;
92
0
    return true;
93
0
  }
94
95
  void OBSquarePlanarStereo::SetConfig(const Config &config)
96
0
  {
97
0
    if (config.center == OBStereo::NoRef) {
98
0
      obErrorLog.ThrowError(__FUNCTION__,
99
0
          "OBSquarePlanarStereo::SetConfig : center id is invalid.", obError);
100
0
      m_cfg = Config();
101
0
      return;
102
0
    }
103
0
    if (config.refs.size() != 4) {
104
0
      std::stringstream ss;
105
0
      ss << "OBSquarePlanarStereo::SetConfig : found " << config.refs.size();
106
0
      ss << " reference ids, should be 4.";
107
0
      obErrorLog.ThrowError(__FUNCTION__, ss.str(), obError);
108
0
      m_cfg = Config();
109
0
      return;
110
0
    }
111
112
    // store using U shape
113
0
    m_cfg = OBTetraPlanarStereo::ToConfig(config, config.refs.at(0), OBStereo::ShapeU);
114
0
  }
115
116
  OBSquarePlanarStereo::Config OBSquarePlanarStereo::GetConfig(OBStereo::Shape shape) const
117
0
  {
118
0
    if (!IsValid())
119
0
      return Config();
120
121
0
    return OBTetraPlanarStereo::ToConfig(m_cfg, m_cfg.refs.at(0), shape);
122
0
  }
123
124
  OBSquarePlanarStereo::Config OBSquarePlanarStereo::GetConfig(unsigned long start,
125
      OBStereo::Shape shape) const
126
0
  {
127
0
    if (!IsValid())
128
0
      return Config();
129
130
0
    return OBTetraPlanarStereo::ToConfig(m_cfg, start, shape);
131
0
  }
132
133
  bool OBSquarePlanarStereo::operator==(const OBSquarePlanarStereo &other) const
134
0
  {
135
0
    if (!IsValid() || !other.IsValid())
136
0
      return false;
137
138
0
    Config u = OBTetraPlanarStereo::ToConfig(other.GetConfig(),
139
0
        m_cfg.refs.at(0), OBStereo::ShapeU);
140
0
    unsigned long a1 = u.refs.at(0);
141
0
    unsigned long b1 = u.refs.at(2);
142
143
0
    if ((a1 == OBStereo::ImplicitRef) && (b1 == OBStereo::ImplicitRef)) {
144
0
      a1 = u.refs.at(1);
145
0
      b1 = u.refs.at(3);
146
0
    }
147
148
0
    if (b1 != OBStereo::ImplicitRef)
149
0
      if (a1 == GetTransRef(b1))
150
0
        return true;
151
0
    if (a1 != OBStereo::ImplicitRef)
152
0
      if (b1 == GetTransRef(a1))
153
0
        return true;
154
155
0
    return false;
156
0
  }
157
158
  bool OBSquarePlanarStereo::IsTrans(unsigned long id1, unsigned long id2) const
159
0
  {
160
0
    return (GetTransRef(id1) == id2);
161
0
  }
162
163
  bool OBSquarePlanarStereo::IsCis(unsigned long id1, unsigned long id2) const
164
0
  {
165
0
    if (m_cfg.refs.size() != 4)
166
0
      return false;
167
168
0
    std::vector<unsigned long> cis = GetCisRefs(id1);
169
0
    if (cis.size() != 2)
170
0
      return false;
171
172
0
    if ((cis.at(0) == id2) || (cis.at(1) == id2))
173
0
      return true;
174
175
0
    return false;
176
0
  }
177
178
  unsigned long OBSquarePlanarStereo::GetTransRef(unsigned long id) const
179
0
  {
180
0
    if (m_cfg.refs.size() != 4)
181
0
      return false;
182
183
    // find id1
184
0
    for (int i = 0; i < 4; ++i) {
185
0
      if (m_cfg.refs.at(i) == id) {
186
        // use it's index to compare id2 with the opposite reference id
187
0
        int j = (i > 1) ? i - 2 : i + 2;
188
0
        return m_cfg.refs.at(j);
189
0
      }
190
0
    }
191
192
    // id not found
193
0
    return OBStereo::NoRef;
194
0
  }
195
196
  std::vector<unsigned long> OBSquarePlanarStereo::GetCisRefs(unsigned long id) const
197
0
  {
198
0
    std::vector<unsigned long> refs;
199
0
    if (m_cfg.refs.size() != 4)
200
0
      return refs;
201
202
    // find id
203
0
    for (int i = 0; i < 4; ++i) {
204
0
      if (m_cfg.refs.at(i) == id) {
205
        // use it's index to get the left/right reference ids
206
0
        int j = (i > 0) ? i - 1 : 3;
207
0
        int k = (i < 3) ? i + 1 : 0;
208
0
        refs.push_back(m_cfg.refs.at(j));
209
0
        refs.push_back(m_cfg.refs.at(k));
210
0
        return refs;
211
0
      }
212
0
    }
213
214
    // id not found
215
0
    return refs;
216
0
  }
217
218
  OBGenericData* OBSquarePlanarStereo::Clone(OBBase *mol) const
219
0
  {
220
0
    OBSquarePlanarStereo *data = new OBSquarePlanarStereo(static_cast<OBMol*>(mol));
221
0
    data->SetConfig(m_cfg);
222
0
    return data;
223
0
  }
224
225
} // namespace OpenBabel
226
227
namespace std {
228
229
  ostream& operator<<(ostream &out, const OpenBabel::OBSquarePlanarStereo &ct)
230
0
  {
231
0
    OpenBabel::OBSquarePlanarStereo::Config cfg = ct.GetConfig();
232
0
    out << "OBSquarePlanarStereo(center = " << cfg.center;
233
234
0
    out << ", refs = ";
235
0
    for (OpenBabel::OBStereo::Refs::iterator i = cfg.refs.begin(); i != cfg.refs.end(); ++i)
236
0
      if (*i != OpenBabel::OBStereo::ImplicitRef)
237
0
        out << *i << " ";
238
0
      else
239
0
        out << "H ";
240
241
0
    switch (cfg.shape) {
242
0
      case OpenBabel::OBStereo::ShapeU:
243
0
        out << ", shape = U)";
244
0
        break;
245
0
      case OpenBabel::OBStereo::ShapeZ:
246
0
        out << ", shape = Z)";
247
0
        break;
248
0
      case OpenBabel::OBStereo::Shape4:
249
0
        out << ", shape = 4)";
250
0
        break;
251
0
    }
252
253
0
    return out;
254
0
  }
255
256
  ostream& operator<<(ostream &out, const OpenBabel::OBSquarePlanarStereo::Config &cfg)
257
0
  {
258
0
    out << "OBSquarePlanarStereo::Config(center = " << cfg.center;
259
260
0
    out << ", refs = ";
261
0
    for (OpenBabel::OBStereo::Refs::const_iterator i = cfg.refs.begin(); i != cfg.refs.end(); ++i)
262
0
      if (*i != OpenBabel::OBStereo::ImplicitRef)
263
0
        out << *i << " ";
264
0
      else
265
0
        out << "H ";
266
267
0
    switch (cfg.shape) {
268
0
      case OpenBabel::OBStereo::ShapeU:
269
0
        out << ", shape = U)";
270
0
        break;
271
0
      case OpenBabel::OBStereo::ShapeZ:
272
0
        out << ", shape = Z)";
273
0
        break;
274
0
      case OpenBabel::OBStereo::Shape4:
275
0
        out << ", shape = 4)";
276
0
        break;
277
0
    }
278
279
0
    return out;
280
0
  }
281
282
} // namespace std
283