GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/CrossLink.cpp
Date: 2025-11-20 01:41:33
Lines:
328/447
73.4%
Functions:
44/46
95.7%
Branches:
239/632
37.8%

Line Branch Exec Source
1 /* BEGIN software license
2 *
3 * MsXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright(C) 2009,...,2018 Filippo Rusconi
6 *
7 * http://www.msxpertsuite.org
8 *
9 * This file is part of the MsXpertSuite project.
10 *
11 * The MsXpertSuite project is the successor of the massXpert project. This
12 * project now includes various independent modules:
13 *
14 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
15 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 * END software license
31 */
32
33 /////////////////////// stdlib includes
34 #include <unordered_map>
35
36
37 /////////////////////// Qt includes
38 #include <QString>
39 #include <QUuid>
40
41
42 /////////////////////// pappsomspp includes
43
44
45 /////////////////////// Local includes
46 #include "MsXpS/libXpertMassCore/CrossLink.hpp"
47 #include "MsXpS/libXpertMassCore/Polymer.hpp"
48
49 int crossLinkMetaTypeId = qRegisterMetaType<MsXpS::libXpertMassCore::CrossLink>(
50 "MsXpS::libXpertMassCore::CrossLink");
51
52 int crossLinkPtrMetaTypeId =
53 qRegisterMetaType<MsXpS::libXpertMassCore::CrossLink *>(
54 "MsXpS::libXpertMassCore::CrossLinkPtr");
55
56 namespace MsXpS
57 {
58 namespace libXpertMassCore
59 {
60
61 /*!
62 \class MsXpS::libXpertMassCore::CrossLink
63 \inmodule libXpertMassCore
64 \ingroup PolChemDefAqueousChemicalReactions
65 \inheaderfile CrossLink.hpp
66
67 \brief The CrossLink class provides abstractions to work with
68 a cross-link entity between \l Monomer instances.
69
70 The notion of a cross-link is that it is a chemical reaction that involves at
71 least two monomers in a \l Polymer or in an \l Oligomer sequence.
72
73 Polymer sequences might contain more than one CrossLink and these are stored in
74 a container.
75 */
76
77
78 /*!
79 \variable MsXpS::libXpertMassCore::CrossLink::mcsp_crossLinker
80
81 \brief The \l CrossLinker instance defining the chemistry of this CrossLink.
82 */
83
84 /*!
85 \variable MsXpS::libXpertMassCore::CrossLink::mcsp_polymer
86
87 \brief The \l Polymer instance of which this CrossLink is part.
88 */
89
90 /*!
91 \variable MsXpS::libXpertMassCore::CrossLink::m_monomers
92
93 \brief The container of \l Monomer instances that are involved in the formation
94 of this CrossLink.
95
96 \note
97
98 The Monomer pointers stored in the m_monomers member are MonomerCstSPtr that
99 point to MonomerSPtr items stored in the \l{Polymer}'s \l{Sequence}'s vector of
100 of MonomerSPtr (authorized implicit to-const cast).
101
102 Because it is not possible to perform the to-non-const back-cast, when needed
103 to access the \l{Sequence}'s MonomerSPtr, the search is performed by providing
104 the Monomer raw pointer managed by the MonomerSPtr in m_monomers.
105
106 The reason why the CrossLink stores the involved \l Monomer instances as
107 pointers to these very Monomer instances in the polymer sequence is that in this
108 way, even if the sequence is edited, the cross-link does not loose the relation
109 to the monomers. The only way that the sequence editing modifies a CrossLink
110 instance is by removing at least one \l Monomer instance involved in the
111 CrossLink. If that occurs, the CrossLink is informed and it is destructed.
112 */
113
114 /*!
115 \variable MsXpS::libXpertMassCore::CrossLink::m_comment
116
117 \brief The comment that might be associated to this CrossLink.
118 */
119
120
121 /*!
122 \typedef MsXpS::libXpertMassCore::CrossLinkSPtr
123
124 \relates MsXpS::libXpertMassCore::CrossLink
125 Synonym for std::shared_ptr<CrossLink>.
126 */
127
128 /*!
129 \typedef MsXpS::libXpertMassCore::CrossLinkCstSPtr
130
131 \relates MsXpS::libXpertMassCore::CrossLink
132 Synonym for std::shared_ptr<const CrossLink>.
133 */
134
135 /*!
136 \typedef MsXpS::libXpertMassCore::CrossLinkWPtr
137
138 \relates MsXpS::libXpertMassCore::CrossLink
139 Synonym for std::weak_ptr<CrossLink>.
140 */
141
142 /*!
143 \typedef MsXpS::libXpertMassCore::CrossLinkCstWPtr
144
145 \relates MsXpS::libXpertMassCore::CrossLink
146 Synonym for std::weak_ptr<const CrossLink>.
147 */
148 /*!
149 \typealias MsXpS::libXpertMassCore::UuidCrossLinkCstWPtrPair
150
151 \relates MsXpS::libXpertMassCore::CrossLink
152 Synonym for std::pair<QString, CrossLinkCstWPtr>.
153 */
154
155 /*!
156 \typealias MsXpS::libXpertMassCore::UuidCrossLinkWPtrPair
157
158 \relates MsXpS::libXpertMassCore::CrossLink
159
160 \brief Synonym for std::pair<QString, CrossLinkWPtr>.
161
162 These pairs are used to store a unique identifier (Uuid) string related to
163 a std::shared_ptr<CrossLink> type. This kind of pair is used in a
164 container in the \l Polymer class. The fact that the std::shared_ptr is
165 converted to a std::weak_ptr is interesting because the item in the pair will
166 not increase the reference count.
167 */
168
169
170 /*!
171 \brief Constructs a totally empty CrossLink instance
172 */
173 2 CrossLink::CrossLink()
174 {
175 2 }
176
177 /*!
178 \brief Constructs a CrossLink instance
179
180 \list
181 \li \a pol_chem_def_csp: the polymer chemistry definition (\l PolChemDef).
182 \li \a polymer_cqsp: the \l Polymer instance in which this CrossLink was formed.
183 \li \a name: the name of this CrossLink instance.
184 \li \a formula: the \l Formula that describes the reaction that is the basis of
185 the chemical reaction leading to the formation of this CrossLink.
186 \li \a comment: a comment that might be associated to this CrossLink.
187 \endlist
188
189 \note Providing a nullptr for \a polymer_cqsp is fatal.
190 */
191 8 CrossLink::CrossLink(PolChemDefCstSPtr pol_chem_def_csp,
192 PolymerCstQSPtr polymer_cqsp,
193 const QString &name,
194 const QString &formula,
195 8 const QString &comment)
196 8 : mcsp_crossLinker(
197 std::make_shared<const CrossLinker>(pol_chem_def_csp, name, formula)),
198
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 mcsp_polymer(polymer_cqsp),
199
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
16 m_comment(comment)
200 {
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(polymer_cqsp == nullptr || polymer_cqsp.get() == nullptr)
202 qFatalStream()
203 << "Programming error. The pointer cannot be nullptr. If that pointer "
204 "was gotten from Polymer::getCstSharedPtr(), ensure that raw pointer "
205 "is "
206 "indeed managed by a std::shared_ptr< Polymer> shared pointer.";
207
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 }
208
209 /*!
210 \brief Constructs a CrossLink instance
211
212 \list
213 \li \a cross_linker_csp: CrossLinker instance used to initialize this CrossLink.
214 \li \a polymer_cqsp: the \l Polymer instance in which this CrossLink was formed
215 \li \a comment: a comment that might be associated to this CrossLink
216 \endlist
217
218 \note Providing a nullptr for \a polymer_cqsp is fatal.
219 */
220 104 CrossLink::CrossLink(CrossLinkerCstSPtr cross_linker_csp,
221 PolymerCstQSPtr polymer_cqsp,
222 104 const QString &comment)
223 104 : mcsp_crossLinker(cross_linker_csp),
224
1/2
✓ Branch 0 taken 104 times.
✗ Branch 1 not taken.
104 mcsp_polymer(polymer_cqsp),
225
3/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 104 times.
166 m_comment(comment)
226 {
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
104 if(polymer_cqsp == nullptr || polymer_cqsp.get() == nullptr)
228 qFatalStream()
229 << "Programming error. The pointer cannot be nullptr. If that pointer "
230 "was gotten from Polymer::getCstSharedPtr(), ensure that raw pointer "
231 "is "
232 "indeed managed by a std::shared_ptr< Polymer> shared pointer.";
233
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
104 }
234
235 /*!
236 \brief Constructs a CrossLink instance as a copy of \a other.
237 */
238 2 CrossLink::CrossLink(const CrossLink &other)
239 2 : mcsp_crossLinker(other.mcsp_crossLinker),
240
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 mcsp_polymer(other.mcsp_polymer),
241
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 m_comment(other.m_comment),
242
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m_monomers(other.m_monomers)
243 {
244
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 }
245
246 /*!
247 \brief Destructs this CrossLink instance.
248
249 No entity needs to be destructed, since the \l Monomer instances in the list
250 are not owned by this CrossLinker instance.
251 */
252 15 CrossLink::~CrossLink()
253 {
254
1/2
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 }
255
256 //////////////// THE POLYMER /////////////////////
257
258 /*!
259 \brief Returns the \l Polymer instance in which this CrossLink has
260 been formed.
261 */
262 PolymerCstQSPtr
263 2 CrossLink::getPolymerCstSPtr() const
264 {
265
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 return mcsp_polymer;
266 }
267
268 //////////////// THE CROSS-LINKER /////////////////////
269
270 /*!
271 \brief Returns the member CrossLinker.
272 */
273 const CrossLinkerCstSPtr
274 18 CrossLink::getCrossLinkerCstSPtr() const
275 {
276 18 return mcsp_crossLinker;
277 }
278
279 //////////////// THE COMMENT /////////////////////
280 /*!
281 \brief Sets the \a comment.
282 */
283 void
284 42 CrossLink::setComment(const QString &comment)
285 {
286 42 m_comment = comment;
287 42 }
288
289 /*!
290 \brief Returns the comment.
291 */
292 const QString &
293 4 CrossLink::getComment() const
294 {
295 4 return m_comment;
296 }
297
298 //////////////// THE NAME /////////////////////
299 /*!
300 \brief Returns the name of the member \l CrossLinker.
301 */
302 QString
303 2 CrossLink::getCrossLinkerName() const
304 {
305
1/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if(mcsp_crossLinker != nullptr)
306
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
2 return mcsp_crossLinker->getName();
307
308 return QString();
309 }
310
311 //////////////// THE MONOMERS /////////////////////
312
313 /*!
314 \brief Returns a const reference to the Monomer container.
315 */
316 const std::vector<MonomerCstSPtr> &
317 2989 CrossLink::getMonomersCstRef() const
318 {
319 2989 return m_monomers;
320 }
321
322 /*!
323 \brief Returns a reference to the Monomer container.
324 */
325 std::vector<MonomerCstSPtr> &
326 4 CrossLink::getMonomersRef()
327 {
328 4 return m_monomers;
329 }
330
331 /*!
332 \brief Adds \a monomer_csp to the member container of Monomer instances and
333 returns the matching QUuid value string.
334 */
335 QString
336 5 CrossLink::appendMonomer(MonomerCstSPtr monomer_csp)
337 {
338 5 return storeMonomer(monomer_csp);
339 }
340
341 /*!
342 \brief Returns the Monomer at \a index in the member container of \l Monomer
343 instances.
344
345 \a index cannot be out of bounds.
346 */
347 MonomerCstSPtr
348 8 CrossLink::getMonomerAt(std::size_t index)
349 {
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(index >= m_monomers.size())
351 qFatalStream() << "Programming error. Index is out of bounds.";
352
353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 return m_monomers.at(index);
354 }
355
356 /*!
357 \brief Removes the Monomer at \a index in the member container of \l Monomer
358 instances.
359
360 \a index cannot be out of bounds.
361
362 Returns true if a Monomer was indeed removed, false otherwise.
363 */
364 bool
365 1 CrossLink::removeMonomerAt(std::size_t index)
366 {
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(index >= m_monomers.size())
368 qFatalStream() << "Programming error. Index is out of bounds.";
369
370 1 MonomerCstSPtr monomer_csp = *(m_monomers.cbegin() + index);
371
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(monomer_csp == nullptr)
373 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
374
375
3/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 return removeMonomer(monomer_csp);
376 1 }
377
378 /*!
379 \brief Removes Monomer \a monomer_csp from the member container of Monomer
380 intances.
381
382 Returns true if a Monomer was indeed removed, false otherwise.
383 */
384 bool
385 1 CrossLink::removeMonomer(MonomerCstSPtr monomer_csp)
386 {
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(monomer_csp == nullptr || monomer_csp.get() == nullptr)
388 qFatalStream() << "Cannot be that pointer is nullptr.";
389
390 // We will need this anyway.
391 1 QString uuid = getUuidForMonomer(monomer_csp);
392
393 1 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
394
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::find_if(m_monomers.begin(),
395 m_monomers.end(),
396
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
3 [monomer_csp](MonomerCstSPtr iter_modif_sp) {
397
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 return iter_modif_sp == monomer_csp;
398
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 });
399
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(the_iterator_cst == m_monomers.end())
401 {
402 qCritical() << "The MonomerCstSPtr was not found in the container.";
403
404 if(!uuid.isEmpty())
405 qFatalStream()
406 << "Inconsistency between m_monomers and m_uuidMonomerPairs.";
407
408 return false;
409 }
410
411 // At this point, both containers contain modif_sp.
412
413 1 m_monomers.erase(the_iterator_cst);
414
415
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::vector<UuidMonomerCstWPtrPair>::const_iterator the_pair_iterator_cst =
416 2 std::find_if(m_uuidMonomerPairs.cbegin(),
417 m_uuidMonomerPairs.cend(),
418
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
5 [uuid](const UuidMonomerCstWPtrPair &the_pair) {
419 // Do not query the modif_sp managed object because it can
420 // be nullptr!
421
3/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
3 return the_pair.first == uuid;
422 });
423
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(the_pair_iterator_cst == m_uuidMonomerPairs.cend())
425 qFatalStream()
426 << "Inconsistency between m_monomers and m_uuidMonomerPairs.";
427
428 1 m_uuidMonomerPairs.erase(the_pair_iterator_cst);
429
430 1 return true;
431 1 }
432
433 /*!
434 \brief Sets in the Monomer container, the Monomer instances occurring at the
435 indices listed in \a monomer_indices_text.
436
437 \a monomer_indices_text contains a list of \l Monomer instance indices separated
438 by ';' characters. The corresponding monomers (MonomerSPtr) found in the member
439 Polymer's Sequence object are copied to the member list of \l Monomer instances
440 as MonomerCstSPtr using the UuidMonomerCstWPtrPair-based logic. This
441 process effectively documents a CrossLink in that member Polymer's Sequence.
442
443 If the process is successful, \a ok is set to true, otherwise it is set to
444 false.
445
446 Returns the new size of the member container of Monomer instances.
447 */
448 std::size_t
449 108 CrossLink::fillInMonomers(QString monomer_indices_text, bool &ok)
450 {
451 108 m_monomers.clear();
452
453 108 QStringList indices_list =
454 108 monomer_indices_text.split(';', Qt::SkipEmptyParts);
455
456 // There must be at least 2 monomers to make a cross-link !
457
458
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 103 times.
108 if(indices_list.size() < 2)
459 {
460
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 qCritical() << "The count of indices must be greater than 2.";
461
462 5 ok = false;
463 5 return 0;
464 }
465
466
4/6
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 103 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 206 times.
✓ Branch 7 taken 103 times.
309 for(auto &index_string : indices_list)
467 {
468 206 bool res = false;
469
1/2
✓ Branch 1 taken 206 times.
✗ Branch 2 not taken.
206 std::size_t index = index_string.toInt(&res);
470
471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if(!res)
472 {
473 ok = false;
474 return 0;
475 }
476
477
2/4
✓ Branch 1 taken 206 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 206 times.
206 if(index >= mcsp_polymer->size())
478 {
479 ok = false;
480 m_monomers.clear();
481 return 0;
482 }
483
484
3/6
✓ Branch 1 taken 206 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 206 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 206 times.
618 storeMonomer(
485
2/4
✓ Branch 1 taken 206 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 206 times.
✗ Branch 5 not taken.
412 mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index));
486 }
487
488 103 ok = true;
489
490 103 return m_monomers.size();
491 108 }
492
493 /*!
494 \brief Returns a container with all the locations of the Monomer instances
495 involved in this CrossLink.
496
497 If \a location_type is Enums::LocationType::INDEX, the locations are numbered as
498 indices. If \a location_type is Enums::LocationType::POSITION, the locations are
499 numbered as positions (that is, index + 1).
500
501 For example, if the cross-link involves Monomer instances at indices 20, 45, 89,
502 then the indices are this continuum of indices: {20;21;22;...;87;88;89}.
503 */
504 std::vector<std::size_t>
505 14 CrossLink::continuumOfLocationsOfInclusiveSequenceMonomers(
506 Enums::LocationType location_type) const
507 {
508 // First get indices.
509 14 std::vector<std::size_t> indices;
510
511 // qDebug() << "There are" << m_monomers.size()
512 // << "Monomer pointers in this cross-link.";
513
514
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 14 times.
54 for(const MonomerCstSPtr &monomer_csp : m_monomers)
515 {
516 40 bool ok = false;
517 40 std::size_t monomer_index =
518
2/4
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 40 times.
✗ Branch 5 not taken.
40 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
519
520
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 if(ok)
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
80 indices.push_back(monomer_index);
522 }
523
524 // Sort the indices.
525 14 std::sort(indices.begin(), indices.end());
526
527 // Now that we have the minIndex and the maxIndex of the region
528 // involved by the cross-link, we can fill-in the integer list
529 // with all the values contained between these two borders.
530
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 std::size_t begin_index = (*indices.cbegin());
532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 std::size_t end_index = (*std::prev(indices.cend()));
533
534 // Only if positions are required, do we increment the indices by one.
535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(location_type == Enums::LocationType::POSITION)
536 {
537 ++begin_index;
538 ++end_index;
539 }
540
541 14 std::vector<std::size_t> locations;
542
543
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 14 times.
150 for(std::size_t iter = begin_index; iter <= end_index; ++iter)
544 {
545 // If we had a cross-link between monomers [4] and [10] of the
546 // polymer, then the indices appended to the list would be 4,
547 // 5, 6, 7, 8, 9 and 10.
548
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 46 times.
272 locations.push_back(iter);
549 }
550
551 14 return locations;
552 14 }
553
554 /*!
555 \brief Returns a string containing a ';'-separated list of the locations of all
556 the \l Monomer instances involved in this CrossLink.
557
558 If \a location_type is Enums::LocationType::INDEX, indices are formatted. If \a
559 location_type is Enums::LocationType::POSITION, positions are formatted (that
560 is, indices + 1).
561 */
562 QString
563 5 CrossLink::continuumOfLocationsOfInclusiveSequenceMonomersAsText(
564 Enums::LocationType location_type) const
565 {
566 5 std::vector<std::size_t> locations =
567 5 continuumOfLocationsOfInclusiveSequenceMonomers(location_type);
568
569
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 return formatContainerOfMonomerLocationsAsText(locations);
570 5 }
571
572 /*!
573 \brief Returns a container with the locations of the two extreme Monomer
574 instances involved in this CrossLink.
575
576 If \a location_type is Enums::LocationType::INDEX, the locations are numbered as
577 indices. If \a location_type is Enums::LocationType::POSITION, the locations are
578 numbered as positions (that is, index + 1).
579
580 For example, if the cross-link involves Monomer instances at indices 20, 45, 89,
581 then the indices are this range of indices: [20--89].
582 */
583 std::vector<std::size_t>
584 8 CrossLink::locationsOfOnlyExtremeSequenceMonomers(
585 Enums::LocationType location_type) const
586 {
587 8 std::vector<std::size_t> continuum_locations =
588 8 continuumOfLocationsOfInclusiveSequenceMonomers(location_type);
589
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(continuum_locations.size() < 2)
591 qFatalStream()
592 << "Programming error. Cannot be that less than two monomers are "
593 "involved in CrossLink.";
594
595
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::vector<std::size_t> extreme_locations;
596 // Only keep first and last.
597
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 extreme_locations.push_back(continuum_locations.front());
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 extreme_locations.push_back(continuum_locations.back());
599
600
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 return extreme_locations;
601 8 }
602
603 /*!
604 \brief Returns a string containing a ';'-separated list of the locations of
605 the two extreme \l Monomer instances involved in this CrossLink.
606
607 If \a location_type is Enums::LocationType::INDEX, indices are formatted. If \a
608 location_type is Enums::LocationType::POSITION, positions are formatted (that
609 is, indices + 1).
610 */
611 QString
612 4 CrossLink::locationsOfOnlyExtremeSequenceMonomersAsText(
613 Enums::LocationType location_type) const
614 {
615 4 std::vector<std::size_t> locations =
616 4 locationsOfOnlyExtremeSequenceMonomers(location_type);
617
618
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 return formatContainerOfMonomerLocationsAsText(locations);
619 4 }
620
621 /*!
622 \brief Returns the index of \a monomer_csp as found in the member container of
623 Monomer instances.
624
625 \a monomer_csp is typically a Monomer found in the member Polymer for which this
626 function establishes if it is involved in this CrossLink.
627
628 If \a monomer_csp is not found, \a ok is set to false and 0 is returned,
629 otherwise \a ok is set to true and the index of the found Monomer is returned.
630 */
631 std::size_t
632 8 CrossLink::monomerIndex(MonomerCstSPtr monomer_csp, bool &ok) const
633 {
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(monomer_csp == nullptr)
635 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
636
637 8 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
638
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 std::find_if(m_monomers.cbegin(),
639 m_monomers.cend(),
640 12 [&](const MonomerCstSPtr &iter_monomer_csp) {
641
3/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
12 return iter_monomer_csp == monomer_csp;
642 });
643
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(the_iterator_cst == m_monomers.cend())
645 {
646 // Let the caller know.
647 ok = false;
648 return 0;
649 }
650
651 // Let the caller know.
652 8 ok = true;
653 8 return std::distance(m_monomers.cbegin(), the_iterator_cst);
654 }
655
656 /*!
657 \brief Returns the index of \a monomer_crp as found in the container of Monomer
658 instances.
659
660 \a monomer_crp is typically a Monomer found in the member Polymer for which this
661 function establishes if it is involved in this CrossLink.
662
663 If \a monomer_crp is not found, \a ok is set to false and 0 is returned,
664 otherwise
665 \a ok is set to true and the index of the found Monomer is returned.
666 */
667 std::size_t
668 8 CrossLink::monomerIndex(MonomerCstRPtr monomer_crp, bool &ok) const
669 {
670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(monomer_crp == nullptr)
671 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
672
673 8 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 std::find_if(m_monomers.cbegin(),
675 m_monomers.cend(),
676 12 [&](const MonomerCstSPtr &iter_monomer_csp) {
677
3/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
12 return iter_monomer_csp.get() == monomer_crp;
678 });
679
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(the_iterator_cst == m_monomers.cend())
681 {
682 // Let the caller know.
683 ok = false;
684 return 0;
685 }
686
687 // Let the caller know.
688 8 ok = true;
689 8 return std::distance(m_monomers.cbegin(), the_iterator_cst);
690 }
691
692 /*!
693 \brief Returns the first \l Monomer instance in the member container of
694 monomers. If the container is empty; returns nullptr.
695 */
696 MonomerCstSPtr
697 32 CrossLink::getFirstMonomer() const
698 {
699
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if(!m_monomers.size())
700 return nullptr;
701
702 32 return m_monomers.front();
703 }
704
705 //////////////// OPERATORS /////////////////////
706 /*!
707 \brief Assigns \a other to this CrossLink instance.
708
709 After having set the member data, this CrossLink instance is validated and the
710 result is set to m_isValid.
711
712 Returns a reference to this CrossLink instance.
713 */
714 CrossLink &
715 2 CrossLink::operator=(const CrossLink &other)
716 {
717
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(&other == this)
718 return *this;
719
720 2 mcsp_crossLinker = other.mcsp_crossLinker;
721 2 mcsp_polymer = other.mcsp_polymer;
722 2 m_comment = other.m_comment;
723
724 // The Monomer instances are deeply duplicated.
725
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for(const MonomerCstSPtr &monomer_csp : other.m_monomers)
726 4 storeMonomer(monomer_csp);
727
728 2 ErrorList error_list;
729
730
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m_isValid = validate(&error_list);
731
732
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(!m_isValid)
733
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 qCritical()
734 << "The assignment operator copying produced a CrossLink that is not "
735
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "valid, with errors:"
736
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 << Utils::joinErrorList(error_list, ", ");
737
738 2 return *this;
739 2 }
740
741 /*!
742 \brief Returns true if this CrossLink instance and \a other are identical,
743 false otherwise.
744
745 The monomers are compared deeply (non on the basis of the pointer value).
746 */
747 bool
748 2 CrossLink::operator==(const CrossLink &other) const
749 {
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(&other == this)
751 return true;
752
753
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if(mcsp_crossLinker != other.mcsp_crossLinker ||
754
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 mcsp_polymer != other.mcsp_polymer || m_comment != other.m_comment)
755 return false;
756
757 // Because we are dealing with Monomer shared pointers to Monomer instances
758 // found in the containing Polymer, we must compare the pointers, not the
759 // Monomer chemical entities.
760
761
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(other.m_monomers.size() != m_monomers.size())
762 return false;
763
764
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 for(std::size_t iter = 0; iter < other.m_monomers.size(); ++iter)
765 {
766
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(*m_monomers.at(iter) != *other.m_monomers.at(iter))
767 {
768 // qDebug() << "At least one Monomer instance differs in both
769 // CrossLink instances.";
770 return false;
771 }
772 }
773
774 return true;
775 }
776
777 /*!
778 \brief Returns true if this CrossLink instance and \a other are different,
779 false otherwise.
780
781 Returns the negated result of operator==(other);
782 */
783 bool
784 1 CrossLink::operator!=(const CrossLink &other) const
785 {
786
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(&other == this)
787 return false;
788
789 1 return !operator==(other);
790 }
791
792 //////////////// CROSS-LINK LOGIC /////////////////////
793 /*!
794 \brief Tells if this CrossLink instance is encompassed (partially or fully) or
795 not at all by the Polymer sequence region defined by the [\a start - \a end ]
796 \l Monomer index range.
797
798 In other words, this function tells if a given set of Monomers are involved in
799 this CrossLink.
800
801 The count of monomers involved in this cross-link that:
802
803 \list
804 \li Are contained in the range is stored in \a in_count.
805 \li Are not contained in the range is stored in \a out_count.
806 \endlist
807
808 If all the monomers are listed as \a in_count, then this CrossLink is fully
809 encompassed by the Polymer sequence region defined by the [\a start - \a end ]
810 \l Monomer index range and Enums::CrossLinkEncompassed::FULLY is returned.
811
812 If all the monomers are listed as \a out_count, then this CrossLink is not at
813 all encompassed by the Polymer sequence region and
814 Enums::CrossLinkEncompassed::NOT is returned.
815
816 If both kinds of monomers were found, then
817 Enums::CrossLinkEncompassed::PARTIALLY is returned.
818 */
819 Enums::CrossLinkEncompassed
820 5 CrossLink::isEncompassedByIndexRange(std::size_t start,
821 std::size_t end,
822 std::size_t &in_count,
823 std::size_t &out_count) const
824 {
825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if(mcsp_polymer == nullptr)
826 qFatalStream()
827 << "Programming error. The Polymer pointer cannot be nullptr.";
828
829 // Iterate in the list of monomers, and for each monomer check if
830 // their index is contained in the stretch.
831
832 5 in_count = 0;
833 5 out_count = 0;
834
835 5 std::size_t local_start = std::min(start, end);
836 5 std::size_t local_end = std::max(start, end);
837
838
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 for(const MonomerCstSPtr &monomer_csp : m_monomers)
839 {
840 10 bool ok = false;
841 10 std::size_t monomer_index =
842 10 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
843
844
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if(ok)
845 {
846
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if(monomer_index >= local_start && monomer_index <= local_end)
847 5 ++in_count;
848 else
849 5 ++out_count;
850 }
851 }
852
853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if((in_count + out_count) != m_monomers.size())
854 qFatalStream()
855 << "The count of monomers in and out should sum to the size of "
856 "the Monomer container.";
857
858
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
5 if(out_count && in_count)
859 return Enums::CrossLinkEncompassed::PARTIALLY;
860
861
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(out_count)
862 return Enums::CrossLinkEncompassed::NOT;
863
864
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(in_count)
865 1 return Enums::CrossLinkEncompassed::FULLY;
866
867 return Enums::CrossLinkEncompassed::NOT;
868 }
869
870 /*!
871 \brief Tells if this CrossLink instance is encompassed (partially or fully) or
872 not at all by various \l IndexRange instances in the \a index_ranges \l
873 IndexRangeCollection.
874
875 In other words, this function tells if a given set of Monomers are involved in
876 this CrossLink. For examle, when \a index_ranges contains a single index range
877 that resolves to a single Monomer index in the Sequence, this function simply
878 says if the CrossLink involves that monomer.
879
880 The count of monomers involved in this cross-link that:
881
882 \list
883 \li Are contained in the regions defined in \a index_ranges is stored in \a
884 in_count.
885 \li Are not contained in the range is stored in \a out_count.
886 \endlist
887
888 If all the monomers are listed as \a in_count, then this CrossLink is fully
889 encompassed by the Polymer sequence regions defined in \a index_ranges and
890 Enums::CrossLinkEncompassed::FULLY is returned.
891
892 If all the monomers are listed as \a out_count, then this CrossLink is not at
893 all encompassed by the Polymer sequence region and
894 Enums::CrossLinkEncompassed::NOT is returned.
895
896 If both kinds of monomers were found, then
897 Enums::CrossLinkEncompassed::PARTIALLY is returned.
898 */
899 Enums::CrossLinkEncompassed
900 1815 CrossLink::isEncompassedByIndexRangeCollection(
901 const IndexRangeCollection &index_ranges,
902 std::size_t &in_count,
903 std::size_t &out_count) const
904 {
905
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1815 times.
1815 if(mcsp_polymer == nullptr)
906 qFatalStream()
907 << "Programming error. The Polymer pointer cannot be nullptr.";
908
909 // Iterate in the list of monomers involved in *this crossLink,
910 // and for each monomer check if their index is contained in any
911 // of the Coordinates [start--end] of the coordinateList passed as
912 // parameter.
913
914 1815 in_count = 0;
915 1815 out_count = 0;
916
917
2/2
✓ Branch 0 taken 3631 times.
✓ Branch 1 taken 1815 times.
5446 for(const MonomerCstSPtr &monomer_csp : m_monomers)
918 {
919 3631 bool was_counted_in = false;
920
921 3631 bool ok = false;
922 3631 qsizetype monomer_index =
923 3631 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
924
925
1/2
✓ Branch 0 taken 3631 times.
✗ Branch 1 not taken.
3631 if(ok)
926 {
927 // Is the index encompassed by any of the SequenceRange instances of
928 // the SequenceRanges ?
929
930
2/2
✓ Branch 2 taken 3668 times.
✓ Branch 3 taken 3412 times.
7080 foreach(const IndexRange *item, index_ranges.getRangesCstRef())
931 {
932
2/2
✓ Branch 0 taken 3017 times.
✓ Branch 1 taken 651 times.
3668 if(monomer_index >= item->m_start &&
933
2/2
✓ Branch 0 taken 219 times.
✓ Branch 1 taken 2798 times.
3017 monomer_index <= item->m_stop)
934 {
935 219 ++in_count;
936 219 was_counted_in = true;
937 219 break;
938 }
939 3631 }
940
941
2/2
✓ Branch 0 taken 3412 times.
✓ Branch 1 taken 219 times.
3631 if(!was_counted_in)
942 3412 ++out_count;
943 }
944 }
945
946
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1815 times.
1815 if((in_count + out_count) != m_monomers.size())
947 qFatalStream()
948 << "The count of monomers in and out should sum to the size of "
949 "the Monomer container.";
950
951
4/4
✓ Branch 0 taken 1759 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 1653 times.
✓ Branch 3 taken 106 times.
1815 if(out_count && in_count)
952 return Enums::CrossLinkEncompassed::PARTIALLY;
953
954
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 1653 times.
1709 if(out_count)
955 return Enums::CrossLinkEncompassed::NOT;
956
957
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 if(in_count)
958 56 return Enums::CrossLinkEncompassed::FULLY;
959
960 return Enums::CrossLinkEncompassed::NOT;
961 }
962
963 //////////////// VALIDATIONS /////////////////////
964 /*!
965 \brief Returns true if this CrossLink instance validates successfully, false
966 otherwise.
967
968 The validation is successful if:
969
970 \list
971 \li The count of \l Monomer instances listed in the member list is > 1.
972
973 \li The member \l Polymer instance exists.
974
975 \li The count of \l Monomer instances listed in the member list is equal to
976 the number of \l Modif instance in the \l CrossLinker base class.
977
978 \li If the list of \l Modif instances in the \l CrossLinker base class is not
979 empty, there must be a colinearity between the order in which these instances
980 are listed and the order in which the \l Monomer instances are listed in the
981 member list. The monomer instance must be a target of the modification.
982 \endlist
983
984 Errors are reported as messages in \a error_list_p.
985 */
986 bool
987 145 CrossLink::validate(ErrorList *error_list_p) const
988 {
989 145 Q_ASSERT(error_list_p != nullptr);
990
991 // Set it as valid, then we negate as we validate.
992 145 m_isValid = true;
993
994
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 144 times.
145 if(m_monomers.size() <= 1)
995 {
996 2 error_list_p->push_back(
997 "A CrossLink involving on one Monomer is invalid");
998
999
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 qCritical() << "A CrossLink involving on one Monomer is invalid.";
1000
1001 1 m_isValid = false;
1002 }
1003
1004
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 145 times.
145 if(mcsp_polymer == nullptr)
1005 {
1006 error_list_p->push_back("A CrossLink with no Polymer is invalid");
1007
1008 qCritical() << "A CrossLink with no Polymer is invalid.";
1009
1010 m_isValid = false;
1011 }
1012
1013
2/2
✓ Branch 0 taken 291 times.
✓ Branch 1 taken 145 times.
436 for(const MonomerCstSPtr &monomer_csp : m_monomers)
1014 {
1015
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 291 times.
291 if(!monomer_csp->validate(error_list_p))
1016 {
1017 error_list_p->push_back(
1018 "A CrossLink involving invalid Monomer instances is invalid");
1019
1020 qCritical()
1021 << "A CrossLink involving invalid Monomer instances is invalid.";
1022
1023 m_isValid = false;
1024 }
1025 }
1026
1027
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 145 times.
145 if(mcsp_crossLinker == nullptr)
1028 {
1029 error_list_p->push_back("A CrossLink with no CrossLinker is invalid");
1030
1031 qCritical() << "A CrossLink with no CrossLinker is invalid.";
1032
1033 m_isValid = false;
1034 return m_isValid;
1035 }
1036 else
1037 {
1038
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 144 times.
145 if(!mcsp_crossLinker->validate(error_list_p))
1039 {
1040 2 error_list_p->push_back(
1041 "A CrossLink with an invalid CrossLinker is invalid");
1042
1043
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 qCritical() << "A CrossLink with an invalid CrossLinker is invalid.";
1044
1045 1 m_isValid = false;
1046 1 return m_isValid;
1047 }
1048
1049 // The CrossLinker might have Modif instances in its container. These
1050 // Modif instances
1051 // need to be compatible with the monomers involved in this CrossLink,
1052 // that is, their targets specifications need to be compatible with the
1053 // Monomers involved in this CrossLink.
1054
1055 144 const std::vector<ModifCstSPtr> &cross_linker_modifs =
1056 144 mcsp_crossLinker->getModifsCstRef();
1057
1058 // Indeed, there is some logic here. Either the m_modifList in the
1059 // parent class CrossLinker contains no item, in which case
1060 // everything is fine, or it does contain items. In the latter case,
1061 // then, the number of items must match the number of monomers being
1062 // crosslinked, and then we get to know which modif is attributable
1063 // to which monomer, hence the possibility of a check.
1064
1065
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 if(cross_linker_modifs.size())
1066 {
1067
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if(cross_linker_modifs.size() != m_monomers.size())
1068 {
1069 error_list_p->push_back(
1070 "A CrossLink where the number of Modif "
1071 "does not match the number of "
1072 "Monomer is invalid");
1073
1074 qCritical() << "A CrossLink where the number of Modif "
1075 "does not match the number of "
1076 "Monomer is invalid.";
1077
1078 m_isValid = false;
1079 return m_isValid;
1080 }
1081
1082 // At this point, we can make the check for each modif/monomer:
1083
2/2
✓ Branch 0 taken 291 times.
✓ Branch 1 taken 144 times.
435 for(std::size_t iter = 0; iter < m_monomers.size(); ++iter)
1084 {
1085 291 MonomerCstSPtr monomer_csp = m_monomers.at(iter);
1086
1087
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 291 times.
✓ Branch 3 taken 291 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 291 times.
291 if(!monomer_csp->isModifTarget(*cross_linker_modifs.at(iter)))
1088 {
1089 error_list_p->push_back(
1090 "A CrossLink where the Monomer is not a target of Modif is "
1091 "invalid");
1092
1093 qCritical() << "A CrossLink where the Monomer is not a "
1094 "target of Modif is invalid";
1095
1096 m_isValid = false;
1097 return m_isValid;
1098 }
1099 291 }
1100 }
1101 }
1102
1103 144 return m_isValid;
1104 }
1105
1106 /*!
1107 \brief Returns the validity status of the CrossLink.
1108 */
1109 bool
1110 1 CrossLink::isValid() const
1111 {
1112 1 return m_isValid;
1113 }
1114
1115 //////////////// MASS OPERATIONS /////////////////////
1116
1117 /*!
1118 \brief Calculates the mass of the CrossLink as the mass of the CrossLinker.
1119
1120 Sets the calculated masses to \a mono and \a avg. If \a isotopic_data_csp, these
1121 reference data are used for the calculation, otherwise those in the PolChemDef
1122 referenced by the CrossLinker are used.
1123
1124 Returns true if the calculation could proceed without error.
1125 */
1126 bool
1127 1 CrossLink::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp,
1128 double &mono,
1129 double &avg) const
1130 {
1131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1132 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
1133
1134 1 return mcsp_crossLinker->calculateMasses(isotopic_data_csp, mono, avg);
1135 }
1136
1137 /*!
1138 \brief Adds this CrossLinker's member masses to \a mono and \a avg,
1139 as compounded by the \a times factor.
1140
1141 Returns a reference to this CrossLink.
1142 */
1143 const CrossLink &
1144 48 CrossLink::accountMasses(double &mono, double &avg, int times) const
1145 {
1146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1147 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
1148
1149 48 mcsp_crossLinker->accountMasses(mono, avg, times);
1150
1151 48 qDebug() << "Done accounting masses for this CrossLink with CrossLinker:"
1152 << mcsp_crossLinker->getName();
1153
1154 48 return *this;
1155 }
1156
1157 /*!
1158 \brief Returns the CrossLinker's mass of the type defined by \a mass_type.
1159 */
1160 double
1161 6 CrossLink::getMass(Enums::MassType mass_type) const
1162 {
1163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1164 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
1165
1166 6 return mcsp_crossLinker->getMass(mass_type);
1167 }
1168
1169 ////////////////UTILS /////////////////////
1170 /*!
1171 \brief Returns an allocated string describing this CrossLink instance.
1172 */
1173 QString
1174 1 CrossLink::prepareResultsTxtString()
1175 {
1176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1177 qFatalStream()
1178 << "Programming error. The CrossLinker pointer cannot be nullptr.";
1179
1180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(mcsp_polymer == nullptr)
1181 qFatalStream()
1182 << "Programming error. The Polymer pointer cannot be nullptr.";
1183
1184 1 QString text;
1185
1186
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 text += QObject::tr(
1187 "\nCross-link:\n"
1188 "===============\n"
1189 "Cross-linker name: %1\n"
1190 "Cross-link comment: %2\n")
1191
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 .arg(mcsp_crossLinker->getName())
1192
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 .arg(m_comment);
1193
1194 1 int iter = 1;
1195
1196
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for(const MonomerCstSPtr &monomer_csp : m_monomers)
1197 {
1198 2 bool ok = false;
1199 2 std::size_t monomer_index =
1200
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
1201
1202
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 text += QObject::tr("Partner %1: %2 at position: %3\n")
1203
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 .arg(iter)
1204
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 .arg(monomer_csp->getCode())
1205
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 .arg(monomer_index + 1);
1206
1207 2 ++iter;
1208 }
1209
1210 1 return text;
1211 }
1212
1213 /*!
1214 \brief Crafts and returns a string in the form ";<location>;<location>;"
1215 corresponding to the values stored in the \a locations container.
1216
1217 This function is used to format either Monomer indices or Monomer positions,
1218 which is why it has the neutral "locations" term in its name.
1219
1220 If the container is empty, an empty string is returned.
1221
1222 \sa continuumOfLocationsOfInclusiveSequenceMonomers(),
1223 locationsOfOnlyExtremeSequenceMonomers(),
1224 locationsOfOnlyExtremeSequenceMonomersAsText()
1225 */
1226 QString
1227 9 CrossLink::formatContainerOfMonomerLocationsAsText(
1228 const std::vector<std::size_t> &locations) const
1229 {
1230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(!locations.size())
1231 return QString();
1232
1233 9 QString text = ";";
1234
1235
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 9 times.
31 for(std::size_t location : locations)
1236
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
44 text += QString("%1;").arg(location);
1237
1238 9 return text;
1239 9 }
1240
1241 /*!
1242 \brief Stores the Monomer instance \a monomer_csp pointer in the member
1243 container.
1244
1245 The \a monomer_csp is stored as is, without duplication.
1246
1247 Returns the Uuid string associated to the stored Monomer.
1248 */
1249 QString
1250 213 CrossLink::storeMonomer(const MonomerCstSPtr &monomer_csp)
1251 {
1252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 213 times.
213 if(monomer_csp == nullptr)
1253 qFatalStream() << "Cannot be that the pointer is nullptr.";
1254
1255 213 qDebug() << "Right before storage, there are currently" << m_monomers.size()
1256 << "monomers.";
1257
1258 // Do not store an item twice.
1259
3/6
✓ Branch 1 taken 213 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 213 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 213 times.
426 if(hasMonomer(monomer_csp) || !getUuidForMonomer(monomer_csp).isEmpty())
1260 qFatalStream()
1261 << "It is prohibited to store the same MonomerCstSPtr more than once.";
1262
1263 // Even if we get a ref to shared_ptr, the reference count increment will
1264 // occur.
1265 213 m_monomers.push_back(monomer_csp);
1266 213 QString uuid = QUuid::createUuid().toString();
1267
1/2
✓ Branch 2 taken 213 times.
✗ Branch 3 not taken.
213 m_uuidMonomerPairs.push_back(UuidMonomerCstWPtrPair(uuid, monomer_csp));
1268
1269 213 qDebug() << "Right after storage, there are currently" << m_monomers.size()
1270 << "monomers.";
1271
1272 213 return uuid;
1273 }
1274
1275 /*!
1276 \brief Returns true if \a monomer_csp was found in the member container of
1277 Monomer instances, false otherwise.
1278 */
1279 bool
1280 451 CrossLink::hasMonomer(const MonomerCstSPtr &monomer_csp) const
1281 {
1282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 451 times.
451 if(monomer_csp == nullptr)
1283 qFatalStream() << "Pointer cannot be nullptr.";
1284
1285 451 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
1286 902 std::find_if(m_monomers.cbegin(),
1287 m_monomers.cend(),
1288
5/10
✓ Branch 1 taken 451 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 451 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 451 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 451 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 451 times.
✗ Branch 15 not taken.
2706 [monomer_csp](const MonomerCstSPtr &the_monomer_csp) {
1289
5/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 24 times.
✓ Branch 11 taken 11 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 218 times.
255 return the_monomer_csp == monomer_csp;
1290 });
1291
1292
2/2
✓ Branch 0 taken 426 times.
✓ Branch 1 taken 25 times.
451 if(the_iterator_cst == m_monomers.cend())
1293 426 return false;
1294
1295 // No sanity checks with getMonomerFromUuid() or getUuidForMonomer()
1296 // because that makes circular calls (these functions make sanity
1297 // checks by calling this hasMonomer().)
1298
1299 return true;
1300 }
1301
1302 /*!
1303 \brief Returns true if \a monomer_csp was found in the member container of
1304 Uuid-Monomer pairs, false otherwise.
1305 */
1306 bool
1307 8 CrossLink::hasUuid(const MonomerCstSPtr &monomer_csp) const
1308 {
1309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(monomer_csp == nullptr)
1310 qFatalStream() << "Pointer cannot be nullptr.";
1311
1312 8 std::vector<UuidMonomerCstWPtrPair>::const_iterator the_iterator_cst =
1313 16 std::find_if(m_uuidMonomerPairs.cbegin(),
1314 m_uuidMonomerPairs.cend(),
1315
5/10
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 8 times.
✗ Branch 16 not taken.
48 [monomer_csp](const UuidMonomerCstWPtrPair &the_pair) {
1316
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 return the_pair.second.lock() == monomer_csp;
1317 });
1318
1319
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if(the_iterator_cst == m_uuidMonomerPairs.cend())
1320 return false;
1321
1322 // Sanity check
1323
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(!hasMonomer(monomer_csp))
1324 qFatalStream()
1325 << "Inconsistency between m_monomers and m_uuidMonomerPairs.";
1326
1327 return true;
1328 }
1329
1330 /*!
1331 \brief Returns the Monomer instance pointer in the member container that is
1332 associated to the \a uuid Uuid string.
1333
1334 If no such Monomer instance pointer is found, nullptr is returned.
1335 */
1336 MonomerCstSPtr
1337 8 CrossLink::getMonomerForUuid(const QString &uuid) const
1338 {
1339 8 qDebug() << "There are currently" << m_monomers.size()
1340 << "monomers. The uuid that is asked for:" << uuid;
1341
1342
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 std::vector<std::pair<QString, MonomerCstWPtr>>::const_iterator
1343 the_iterator_cst =
1344 16 std::find_if(m_uuidMonomerPairs.cbegin(),
1345 m_uuidMonomerPairs.cend(),
1346
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
72 [uuid](const UuidMonomerCstWPtrPair &the_pair) {
1347
1/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
8 return the_pair.first == uuid;
1348 });
1349
1350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(the_iterator_cst == m_uuidMonomerPairs.cend())
1351 return nullptr;
1352
1353 8 MonomerCstSPtr monomer_csp = (*the_iterator_cst).second.lock();
1354
1355 // Sanity check
1356
1357
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if(!hasMonomer(monomer_csp))
1358 qFatalStream()
1359 << "Inconsistency between m_monomers and m_uuidMonomerPairs.";
1360
1361 8 return monomer_csp;
1362 8 }
1363
1364 /*!
1365 \brief Returns the UUID string identifying \a monomer_csp in the member
1366 container.
1367
1368 If no such Monomer is found, an empty string is returned.
1369 */
1370 QString
1371 222 CrossLink::getUuidForMonomer(const MonomerCstSPtr &monomer_csp) const
1372 {
1373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 222 times.
222 if(monomer_csp == nullptr)
1374 qFatalStream() << "Pointer cannot be nullptr.";
1375
1376 222 std::vector<UuidMonomerCstWPtrPair>::const_iterator the_iterator_cst =
1377 444 std::find_if(m_uuidMonomerPairs.cbegin(),
1378 m_uuidMonomerPairs.cend(),
1379
5/10
✓ Branch 2 taken 222 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 222 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 222 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 222 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 222 times.
✗ Branch 16 not taken.
1332 [monomer_csp](const UuidMonomerCstWPtrPair &the_pair) {
1380 // Do not query the monomer_sp managed object because it can
1381 // be nullptr!
1382
1/2
✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
125 return the_pair.second.lock() == monomer_csp;
1383 });
1384
1385
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 9 times.
222 if(the_iterator_cst == m_uuidMonomerPairs.cend())
1386 {
1387 // Sanity check
1388
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 213 times.
213 if(hasMonomer(monomer_csp))
1389 qFatalStream() << "Inconsistency between the m_monomers and the "
1390 "m_uuidMonomerPairs vectors.";
1391
1392 213 return QString();
1393 }
1394
1395 // Sanity check
1396
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if(!hasMonomer(monomer_csp))
1397 qFatalStream() << "Inconsistency between the m_monomers and the "
1398 "m_uuidMonomerPairs vectors.";
1399
1400
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
231 return (*the_iterator_cst).first;
1401 }
1402
1403 /*!
1404 \brief Returns a container of QString instances that correspond to the UUID
1405 strings that identify all the Monomer instances involved in this CrossLink.
1406
1407 If no Monomer is found, an empty container is returned.
1408 */
1409 std::vector<QString>
1410 8 CrossLink::getAllMonomerUuids() const
1411 {
1412 8 std::vector<QString> the_uuid_strings;
1413
1414
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
24 for(const UuidMonomerCstWPtrPair &pair : m_uuidMonomerPairs)
1415
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 the_uuid_strings.push_back(pair.first);
1416
1417 // Sanity check
1418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(the_uuid_strings.size() != m_monomers.size())
1419 qFatalStream()
1420 << "Inconsistency between the <object>_s and <uuid-object> pairs.";
1421
1422 8 return the_uuid_strings;
1423 }
1424
1425 /*!
1426 \brief Returns a text string describing this CrossLink instance.
1427 */
1428 QString
1429 CrossLink::toString() const
1430 {
1431 QString text =
1432 QString("CrossLink object of CrossLinker: %1 - ").arg(getCrossLinkerName());
1433
1434 text +=
1435 QString("with %1 involved monomers at indices [").arg(m_monomers.size());
1436
1437 bool ok = false;
1438
1439 for(MonomerCstSPtr monomer_csp : m_monomers)
1440 {
1441 std::size_t index =
1442 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp, ok);
1443
1444 if(!ok)
1445 qFatalStream() << "Programming error.";
1446
1447 text += QString(";%1;").arg(index);
1448 }
1449
1450 text += "]";
1451
1452 return text;
1453 }
1454
1455 /*!
1456 \brief Removes from the member container all the Monomer instance pointers that
1457 are not found to still be alive.
1458 */
1459 void
1460 CrossLink::cleanupMonomers()
1461 {
1462 qDebug() << "At beginning, count of UUID-Monomer pairs:"
1463 << m_uuidMonomerPairs.size();
1464
1465 std::vector<UuidMonomerCstWPtrPair>::iterator the_iterator =
1466 m_uuidMonomerPairs.begin();
1467 std::vector<UuidMonomerCstWPtrPair>::iterator the_end_iterator =
1468 m_uuidMonomerPairs.end();
1469
1470 while(the_iterator != the_end_iterator)
1471 {
1472 if((*the_iterator).second.expired() ||
1473 (*the_iterator).second.lock() == nullptr ||
1474 !hasMonomer((*the_iterator).second.lock()))
1475 the_iterator = m_uuidMonomerPairs.erase(the_iterator);
1476 else
1477 ++the_iterator;
1478 }
1479
1480 qDebug() << "At end, count of UUID-Monomer pairs:"
1481 << m_uuidMonomerPairs.size();
1482 }
1483
1484 } // namespace libXpertMassCore
1485 } // namespace MsXpS
1486