GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/CrossLink.cpp
Date: 2025-11-19 15:26:11
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 6 CrossLink::CrossLink()
174 {
175 6 }
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 24 CrossLink::CrossLink(PolChemDefCstSPtr pol_chem_def_csp,
192 PolymerCstQSPtr polymer_cqsp,
193 const QString &name,
194 const QString &formula,
195 24 const QString &comment)
196 24 : mcsp_crossLinker(
197 std::make_shared<const CrossLinker>(pol_chem_def_csp, name, formula)),
198
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 mcsp_polymer(polymer_cqsp),
199
2/4
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
48 m_comment(comment)
200 {
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 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.
24 }
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 312 CrossLink::CrossLink(CrossLinkerCstSPtr cross_linker_csp,
221 PolymerCstQSPtr polymer_cqsp,
222 312 const QString &comment)
223 312 : mcsp_crossLinker(cross_linker_csp),
224
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 mcsp_polymer(polymer_cqsp),
225
3/4
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 312 times.
498 m_comment(comment)
226 {
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 312 times.
312 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.
312 }
234
235 /*!
236 \brief Constructs a CrossLink instance as a copy of \a other.
237 */
238 6 CrossLink::CrossLink(const CrossLink &other)
239 6 : mcsp_crossLinker(other.mcsp_crossLinker),
240
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 mcsp_polymer(other.mcsp_polymer),
241
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 m_comment(other.m_comment),
242
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 m_monomers(other.m_monomers)
243 {
244
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 }
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 45 CrossLink::~CrossLink()
253 {
254
1/2
✓ Branch 4 taken 45 times.
✗ Branch 5 not taken.
45 }
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 6 CrossLink::getPolymerCstSPtr() const
264 {
265
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 return mcsp_polymer;
266 }
267
268 //////////////// THE CROSS-LINKER /////////////////////
269
270 /*!
271 \brief Returns the member CrossLinker.
272 */
273 const CrossLinkerCstSPtr
274 54 CrossLink::getCrossLinkerCstSPtr() const
275 {
276 54 return mcsp_crossLinker;
277 }
278
279 //////////////// THE COMMENT /////////////////////
280 /*!
281 \brief Sets the \a comment.
282 */
283 void
284 126 CrossLink::setComment(const QString &comment)
285 {
286 126 m_comment = comment;
287 126 }
288
289 /*!
290 \brief Returns the comment.
291 */
292 const QString &
293 12 CrossLink::getComment() const
294 {
295 12 return m_comment;
296 }
297
298 //////////////// THE NAME /////////////////////
299 /*!
300 \brief Returns the name of the member \l CrossLinker.
301 */
302 QString
303 6 CrossLink::getCrossLinkerName() const
304 {
305
1/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 if(mcsp_crossLinker != nullptr)
306
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
6 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 8967 CrossLink::getMonomersCstRef() const
318 {
319 8967 return m_monomers;
320 }
321
322 /*!
323 \brief Returns a reference to the Monomer container.
324 */
325 std::vector<MonomerCstSPtr> &
326 12 CrossLink::getMonomersRef()
327 {
328 12 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 15 CrossLink::appendMonomer(MonomerCstSPtr monomer_csp)
337 {
338 15 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 24 CrossLink::getMonomerAt(std::size_t index)
349 {
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 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 24 times.
24 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 3 CrossLink::removeMonomerAt(std::size_t index)
366 {
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(index >= m_monomers.size())
368 qFatalStream() << "Programming error. Index is out of bounds.";
369
370 3 MonomerCstSPtr monomer_csp = *(m_monomers.cbegin() + index);
371
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(monomer_csp == nullptr)
373 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
374
375
3/8
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
6 return removeMonomer(monomer_csp);
376 3 }
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 3 CrossLink::removeMonomer(MonomerCstSPtr monomer_csp)
386 {
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(monomer_csp == nullptr || monomer_csp.get() == nullptr)
388 qFatalStream() << "Cannot be that pointer is nullptr.";
389
390 // We will need this anyway.
391 3 QString uuid = getUuidForMonomer(monomer_csp);
392
393 3 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
394
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 std::find_if(m_monomers.begin(),
395 m_monomers.end(),
396
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
9 [monomer_csp](MonomerCstSPtr iter_modif_sp) {
397
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 return iter_modif_sp == monomer_csp;
398
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 });
399
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 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 3 m_monomers.erase(the_iterator_cst);
414
415
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 std::vector<UuidMonomerCstWPtrPair>::const_iterator the_pair_iterator_cst =
416 6 std::find_if(m_uuidMonomerPairs.cbegin(),
417 m_uuidMonomerPairs.cend(),
418
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
15 [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 3 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
9 return the_pair.first == uuid;
422 });
423
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(the_pair_iterator_cst == m_uuidMonomerPairs.cend())
425 qFatalStream()
426 << "Inconsistency between m_monomers and m_uuidMonomerPairs.";
427
428 3 m_uuidMonomerPairs.erase(the_pair_iterator_cst);
429
430 3 return true;
431 3 }
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 324 CrossLink::fillInMonomers(QString monomer_indices_text, bool &ok)
450 {
451 324 m_monomers.clear();
452
453 324 QStringList indices_list =
454 324 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 15 times.
✓ Branch 1 taken 309 times.
324 if(indices_list.size() < 2)
459 {
460
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 qCritical() << "The count of indices must be greater than 2.";
461
462 15 ok = false;
463 15 return 0;
464 }
465
466
4/6
✓ Branch 1 taken 309 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 309 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 618 times.
✓ Branch 7 taken 309 times.
927 for(auto &index_string : indices_list)
467 {
468 618 bool res = false;
469
1/2
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
618 std::size_t index = index_string.toInt(&res);
470
471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 618 times.
618 if(!res)
472 {
473 ok = false;
474 return 0;
475 }
476
477
2/4
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 618 times.
618 if(index >= mcsp_polymer->size())
478 {
479 ok = false;
480 m_monomers.clear();
481 return 0;
482 }
483
484
3/6
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 618 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 618 times.
1854 storeMonomer(
485
2/4
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 618 times.
✗ Branch 5 not taken.
1236 mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index));
486 }
487
488 309 ok = true;
489
490 309 return m_monomers.size();
491 324 }
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 42 CrossLink::continuumOfLocationsOfInclusiveSequenceMonomers(
506 Enums::LocationType location_type) const
507 {
508 // First get indices.
509 42 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 120 times.
✓ Branch 1 taken 42 times.
162 for(const MonomerCstSPtr &monomer_csp : m_monomers)
515 {
516 120 bool ok = false;
517 120 std::size_t monomer_index =
518
2/4
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 120 times.
✗ Branch 5 not taken.
120 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
519
520
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if(ok)
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
240 indices.push_back(monomer_index);
522 }
523
524 // Sort the indices.
525 42 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 42 times.
42 std::size_t begin_index = (*indices.cbegin());
532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 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 42 times.
42 if(location_type == Enums::LocationType::POSITION)
536 {
537 ++begin_index;
538 ++end_index;
539 }
540
541 42 std::vector<std::size_t> locations;
542
543
2/2
✓ Branch 0 taken 408 times.
✓ Branch 1 taken 42 times.
450 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 270 times.
✓ Branch 1 taken 138 times.
816 locations.push_back(iter);
549 }
550
551 42 return locations;
552 42 }
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 15 CrossLink::continuumOfLocationsOfInclusiveSequenceMonomersAsText(
564 Enums::LocationType location_type) const
565 {
566 15 std::vector<std::size_t> locations =
567 15 continuumOfLocationsOfInclusiveSequenceMonomers(location_type);
568
569
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 return formatContainerOfMonomerLocationsAsText(locations);
570 15 }
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 24 CrossLink::locationsOfOnlyExtremeSequenceMonomers(
585 Enums::LocationType location_type) const
586 {
587 24 std::vector<std::size_t> continuum_locations =
588 24 continuumOfLocationsOfInclusiveSequenceMonomers(location_type);
589
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 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 24 times.
✗ Branch 2 not taken.
24 std::vector<std::size_t> extreme_locations;
596 // Only keep first and last.
597
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 extreme_locations.push_back(continuum_locations.front());
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 extreme_locations.push_back(continuum_locations.back());
599
600
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 return extreme_locations;
601 24 }
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 12 CrossLink::locationsOfOnlyExtremeSequenceMonomersAsText(
613 Enums::LocationType location_type) const
614 {
615 12 std::vector<std::size_t> locations =
616 12 locationsOfOnlyExtremeSequenceMonomers(location_type);
617
618
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 return formatContainerOfMonomerLocationsAsText(locations);
619 12 }
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 24 CrossLink::monomerIndex(MonomerCstSPtr monomer_csp, bool &ok) const
633 {
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(monomer_csp == nullptr)
635 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
636
637 24 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
638
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 std::find_if(m_monomers.cbegin(),
639 m_monomers.cend(),
640 36 [&](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 12 times.
✓ Branch 9 taken 12 times.
✓ Branch 10 taken 12 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
36 return iter_monomer_csp == monomer_csp;
642 });
643
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 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 24 ok = true;
653 24 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 24 CrossLink::monomerIndex(MonomerCstRPtr monomer_crp, bool &ok) const
669 {
670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(monomer_crp == nullptr)
671 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
672
673 24 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 std::find_if(m_monomers.cbegin(),
675 m_monomers.cend(),
676 36 [&](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 12 times.
✓ Branch 9 taken 12 times.
✓ Branch 10 taken 12 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
36 return iter_monomer_csp.get() == monomer_crp;
678 });
679
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 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 24 ok = true;
689 24 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 96 CrossLink::getFirstMonomer() const
698 {
699
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if(!m_monomers.size())
700 return nullptr;
701
702 96 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 6 CrossLink::operator=(const CrossLink &other)
716 {
717
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(&other == this)
718 return *this;
719
720 6 mcsp_crossLinker = other.mcsp_crossLinker;
721 6 mcsp_polymer = other.mcsp_polymer;
722 6 m_comment = other.m_comment;
723
724 // The Monomer instances are deeply duplicated.
725
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for(const MonomerCstSPtr &monomer_csp : other.m_monomers)
726 12 storeMonomer(monomer_csp);
727
728 6 ErrorList error_list;
729
730
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 m_isValid = validate(&error_list);
731
732
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if(!m_isValid)
733
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 qCritical()
734 << "The assignment operator copying produced a CrossLink that is not "
735
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "valid, with errors:"
736
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
6 << Utils::joinErrorList(error_list, ", ");
737
738 6 return *this;
739 6 }
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 6 CrossLink::operator==(const CrossLink &other) const
749 {
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(&other == this)
751 return true;
752
753
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 if(mcsp_crossLinker != other.mcsp_crossLinker ||
754
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 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 6 times.
6 if(other.m_monomers.size() != m_monomers.size())
762 return false;
763
764
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 for(std::size_t iter = 0; iter < other.m_monomers.size(); ++iter)
765 {
766
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
12 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 3 CrossLink::operator!=(const CrossLink &other) const
785 {
786
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(&other == this)
787 return false;
788
789 3 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 15 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 15 times.
15 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 15 in_count = 0;
833 15 out_count = 0;
834
835 15 std::size_t local_start = std::min(start, end);
836 15 std::size_t local_end = std::max(start, end);
837
838
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 15 times.
45 for(const MonomerCstSPtr &monomer_csp : m_monomers)
839 {
840 30 bool ok = false;
841 30 std::size_t monomer_index =
842 30 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
843
844
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 if(ok)
845 {
846
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 if(monomer_index >= local_start && monomer_index <= local_end)
847 15 ++in_count;
848 else
849 15 ++out_count;
850 }
851 }
852
853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 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 12 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 9 times.
15 if(out_count && in_count)
859 return Enums::CrossLinkEncompassed::PARTIALLY;
860
861
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if(out_count)
862 return Enums::CrossLinkEncompassed::NOT;
863
864
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(in_count)
865 3 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 5445 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 5445 times.
5445 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 5445 in_count = 0;
915 5445 out_count = 0;
916
917
2/2
✓ Branch 0 taken 10893 times.
✓ Branch 1 taken 5445 times.
16338 for(const MonomerCstSPtr &monomer_csp : m_monomers)
918 {
919 10893 bool was_counted_in = false;
920
921 10893 bool ok = false;
922 10893 qsizetype monomer_index =
923 10893 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
924
925
1/2
✓ Branch 0 taken 10893 times.
✗ Branch 1 not taken.
10893 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 11004 times.
✓ Branch 3 taken 10236 times.
21240 foreach(const IndexRange *item, index_ranges.getRangesCstRef())
931 {
932
2/2
✓ Branch 0 taken 9051 times.
✓ Branch 1 taken 1953 times.
11004 if(monomer_index >= item->m_start &&
933
2/2
✓ Branch 0 taken 657 times.
✓ Branch 1 taken 8394 times.
9051 monomer_index <= item->m_stop)
934 {
935 657 ++in_count;
936 657 was_counted_in = true;
937 657 break;
938 }
939 10893 }
940
941
2/2
✓ Branch 0 taken 10236 times.
✓ Branch 1 taken 657 times.
10893 if(!was_counted_in)
942 10236 ++out_count;
943 }
944 }
945
946
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5445 times.
5445 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 5277 times.
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 4959 times.
✓ Branch 3 taken 318 times.
5445 if(out_count && in_count)
952 return Enums::CrossLinkEncompassed::PARTIALLY;
953
954
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 4959 times.
5127 if(out_count)
955 return Enums::CrossLinkEncompassed::NOT;
956
957
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 if(in_count)
958 168 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 435 CrossLink::validate(ErrorList *error_list_p) const
988 {
989 435 Q_ASSERT(error_list_p != nullptr);
990
991 // Set it as valid, then we negate as we validate.
992 435 m_isValid = true;
993
994
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 432 times.
435 if(m_monomers.size() <= 1)
995 {
996 6 error_list_p->push_back(
997 "A CrossLink involving on one Monomer is invalid");
998
999
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 qCritical() << "A CrossLink involving on one Monomer is invalid.";
1000
1001 3 m_isValid = false;
1002 }
1003
1004
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 435 times.
435 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 873 times.
✓ Branch 1 taken 435 times.
1308 for(const MonomerCstSPtr &monomer_csp : m_monomers)
1014 {
1015
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 873 times.
873 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 435 times.
435 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 3 times.
✓ Branch 2 taken 432 times.
435 if(!mcsp_crossLinker->validate(error_list_p))
1039 {
1040 6 error_list_p->push_back(
1041 "A CrossLink with an invalid CrossLinker is invalid");
1042
1043
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 qCritical() << "A CrossLink with an invalid CrossLinker is invalid.";
1044
1045 3 m_isValid = false;
1046 3 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 432 const std::vector<ModifCstSPtr> &cross_linker_modifs =
1056 432 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 432 times.
✗ Branch 1 not taken.
432 if(cross_linker_modifs.size())
1066 {
1067
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 432 times.
432 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 873 times.
✓ Branch 1 taken 432 times.
1305 for(std::size_t iter = 0; iter < m_monomers.size(); ++iter)
1084 {
1085 873 MonomerCstSPtr monomer_csp = m_monomers.at(iter);
1086
1087
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 873 times.
✓ Branch 3 taken 873 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 873 times.
873 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 873 }
1100 }
1101 }
1102
1103 432 return m_isValid;
1104 }
1105
1106 /*!
1107 \brief Returns the validity status of the CrossLink.
1108 */
1109 bool
1110 3 CrossLink::isValid() const
1111 {
1112 3 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 3 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 3 times.
3 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1132 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
1133
1134 3 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 144 CrossLink::accountMasses(double &mono, double &avg, int times) const
1145 {
1146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1147 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
1148
1149 144 mcsp_crossLinker->accountMasses(mono, avg, times);
1150
1151 144 qDebug() << "Done accounting masses for this CrossLink with CrossLinker:"
1152 << mcsp_crossLinker->getName();
1153
1154 144 return *this;
1155 }
1156
1157 /*!
1158 \brief Returns the CrossLinker's mass of the type defined by \a mass_type.
1159 */
1160 double
1161 18 CrossLink::getMass(Enums::MassType mass_type) const
1162 {
1163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr)
1164 qFatalStream() << "Programming error. The pointer cannot be nullptr.";
1165
1166 18 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 3 CrossLink::prepareResultsTxtString()
1175 {
1176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 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 3 times.
3 if(mcsp_polymer == nullptr)
1181 qFatalStream()
1182 << "Programming error. The Polymer pointer cannot be nullptr.";
1183
1184 3 QString text;
1185
1186
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 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 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 .arg(mcsp_crossLinker->getName())
1192
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 .arg(m_comment);
1193
1194 3 int iter = 1;
1195
1196
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for(const MonomerCstSPtr &monomer_csp : m_monomers)
1197 {
1198 6 bool ok = false;
1199 6 std::size_t monomer_index =
1200
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok);
1201
1202
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 text += QObject::tr("Partner %1: %2 at position: %3\n")
1203
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 .arg(iter)
1204
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
12 .arg(monomer_csp->getCode())
1205
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 .arg(monomer_index + 1);
1206
1207 6 ++iter;
1208 }
1209
1210 3 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 27 CrossLink::formatContainerOfMonomerLocationsAsText(
1228 const std::vector<std::size_t> &locations) const
1229 {
1230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if(!locations.size())
1231 return QString();
1232
1233 27 QString text = ";";
1234
1235
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 27 times.
93 for(std::size_t location : locations)
1236
2/4
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 66 times.
✗ Branch 5 not taken.
132 text += QString("%1;").arg(location);
1237
1238 27 return text;
1239 27 }
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 639 CrossLink::storeMonomer(const MonomerCstSPtr &monomer_csp)
1251 {
1252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 639 times.
639 if(monomer_csp == nullptr)
1253 qFatalStream() << "Cannot be that the pointer is nullptr.";
1254
1255 639 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 639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 639 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 639 times.
1278 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 639 m_monomers.push_back(monomer_csp);
1266 639 QString uuid = QUuid::createUuid().toString();
1267
1/2
✓ Branch 2 taken 639 times.
✗ Branch 3 not taken.
639 m_uuidMonomerPairs.push_back(UuidMonomerCstWPtrPair(uuid, monomer_csp));
1268
1269 639 qDebug() << "Right after storage, there are currently" << m_monomers.size()
1270 << "monomers.";
1271
1272 639 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 1353 CrossLink::hasMonomer(const MonomerCstSPtr &monomer_csp) const
1281 {
1282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if(monomer_csp == nullptr)
1283 qFatalStream() << "Pointer cannot be nullptr.";
1284
1285 1353 std::vector<MonomerCstSPtr>::const_iterator the_iterator_cst =
1286 2706 std::find_if(m_monomers.cbegin(),
1287 m_monomers.cend(),
1288
5/10
✓ Branch 1 taken 1353 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1353 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1353 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1353 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1353 times.
✗ Branch 15 not taken.
8118 [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 3 times.
✓ Branch 10 taken 72 times.
✓ Branch 11 taken 33 times.
✓ Branch 12 taken 3 times.
✓ Branch 13 taken 654 times.
765 return the_monomer_csp == monomer_csp;
1290 });
1291
1292
2/2
✓ Branch 0 taken 1278 times.
✓ Branch 1 taken 75 times.
1353 if(the_iterator_cst == m_monomers.cend())
1293 1278 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 24 CrossLink::hasUuid(const MonomerCstSPtr &monomer_csp) const
1308 {
1309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(monomer_csp == nullptr)
1310 qFatalStream() << "Pointer cannot be nullptr.";
1311
1312 24 std::vector<UuidMonomerCstWPtrPair>::const_iterator the_iterator_cst =
1313 48 std::find_if(m_uuidMonomerPairs.cbegin(),
1314 m_uuidMonomerPairs.cend(),
1315
5/10
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 24 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 24 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 24 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 24 times.
✗ Branch 16 not taken.
144 [monomer_csp](const UuidMonomerCstWPtrPair &the_pair) {
1316
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 return the_pair.second.lock() == monomer_csp;
1317 });
1318
1319
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if(the_iterator_cst == m_uuidMonomerPairs.cend())
1320 return false;
1321
1322 // Sanity check
1323
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 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 24 CrossLink::getMonomerForUuid(const QString &uuid) const
1338 {
1339 24 qDebug() << "There are currently" << m_monomers.size()
1340 << "monomers. The uuid that is asked for:" << uuid;
1341
1342
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 std::vector<std::pair<QString, MonomerCstWPtr>>::const_iterator
1343 the_iterator_cst =
1344 48 std::find_if(m_uuidMonomerPairs.cbegin(),
1345 m_uuidMonomerPairs.cend(),
1346
4/8
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 24 times.
✗ Branch 9 not taken.
216 [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 24 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
24 return the_pair.first == uuid;
1348 });
1349
1350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(the_iterator_cst == m_uuidMonomerPairs.cend())
1351 return nullptr;
1352
1353 24 MonomerCstSPtr monomer_csp = (*the_iterator_cst).second.lock();
1354
1355 // Sanity check
1356
1357
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
24 if(!hasMonomer(monomer_csp))
1358 qFatalStream()
1359 << "Inconsistency between m_monomers and m_uuidMonomerPairs.";
1360
1361 24 return monomer_csp;
1362 24 }
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 666 CrossLink::getUuidForMonomer(const MonomerCstSPtr &monomer_csp) const
1372 {
1373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 666 times.
666 if(monomer_csp == nullptr)
1374 qFatalStream() << "Pointer cannot be nullptr.";
1375
1376 666 std::vector<UuidMonomerCstWPtrPair>::const_iterator the_iterator_cst =
1377 1332 std::find_if(m_uuidMonomerPairs.cbegin(),
1378 m_uuidMonomerPairs.cend(),
1379
5/10
✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 666 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 666 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 666 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 666 times.
✗ Branch 16 not taken.
3996 [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 375 times.
✗ Branch 2 not taken.
375 return the_pair.second.lock() == monomer_csp;
1383 });
1384
1385
2/2
✓ Branch 0 taken 639 times.
✓ Branch 1 taken 27 times.
666 if(the_iterator_cst == m_uuidMonomerPairs.cend())
1386 {
1387 // Sanity check
1388
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 639 times.
639 if(hasMonomer(monomer_csp))
1389 qFatalStream() << "Inconsistency between the m_monomers and the "
1390 "m_uuidMonomerPairs vectors.";
1391
1392 639 return QString();
1393 }
1394
1395 // Sanity check
1396
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 if(!hasMonomer(monomer_csp))
1397 qFatalStream() << "Inconsistency between the m_monomers and the "
1398 "m_uuidMonomerPairs vectors.";
1399
1400
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
693 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 24 CrossLink::getAllMonomerUuids() const
1411 {
1412 24 std::vector<QString> the_uuid_strings;
1413
1414
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24 times.
72 for(const UuidMonomerCstWPtrPair &pair : m_uuidMonomerPairs)
1415
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 the_uuid_strings.push_back(pair.first);
1416
1417 // Sanity check
1418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(the_uuid_strings.size() != m_monomers.size())
1419 qFatalStream()
1420 << "Inconsistency between the <object>_s and <uuid-object> pairs.";
1421
1422 24 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