GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/IndexRangeCollection.cpp
Date: 2025-11-19 15:26:11
Lines:
206/257
80.2%
Functions:
34/43
79.1%
Branches:
138/328
42.1%

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 indepstopent 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
34 /////////////////////// Qt includes
35 #include <QDebug>
36 #include <QStringList>
37 #include <QRegularExpression>
38 #include <QRegularExpressionMatch>
39
40 /////////////////////// Local includes
41 #include "MsXpS/libXpertMassCore/globals.hpp"
42 #include "MsXpS/libXpertMassCore/Utils.hpp"
43 #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp"
44
45 namespace MsXpS
46 {
47 namespace libXpertMassCore
48 {
49
50 /*!
51 \class MsXpS::libXpertMassCore::IndexRangeCollection
52 \inmodule libXpertMassCore
53 \ingroup XpertMassCalculations
54 \inheaderfile IndexRangeCollection.hpp
55
56 \brief The IndexRangeCollection class provides a collection of IndexRange
57 instances that enable delimiting \l{Sequence} regions of interest in a given
58 \l{Polymer} instance.
59
60 \sa IndexRange
61 */
62
63
64 /*!
65 \variable MsXpS::libXpertMassCore::IndexRangeCollection::m_ranges
66
67 \brief The container of \l{IndexRange} instances.
68 */
69
70 /*!
71 \variable MsXpS::libXpertMassCore::IndexRangeCollection::m_comment
72
73 \brief A comment that can be associated to this IndexRangeCollection instance..
74 */
75
76 /*!
77 \brief Constructs an empty IndexRangeCollection instance.
78 */
79 774 IndexRangeCollection::IndexRangeCollection(QObject *parent): QObject(parent)
80 {
81 774 }
82
83 /*!
84 \brief Constructs an IndexRangeCollection instance with a single IndexRange
85 object using \a index_start and \a index_stop.
86 */
87 2133 IndexRangeCollection::IndexRangeCollection(qsizetype index_start,
88 qsizetype index_stop,
89 2133 QObject *parent)
90
1/2
✓ Branch 2 taken 2133 times.
✗ Branch 3 not taken.
2133 : QObject(parent)
91 {
92
2/4
✓ Branch 1 taken 2133 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2133 times.
✗ Branch 5 not taken.
2133 IndexRange *index_range_p = new IndexRange(index_start, index_stop, this);
93
94
1/2
✓ Branch 1 taken 2133 times.
✗ Branch 2 not taken.
2133 m_ranges.append(index_range_p);
95 2133 }
96
97 /*!
98 \brief Constructs an IndexRangeCollection instance using \a index_ranges_string
99
100 \sa parseIndexRanges()
101 */
102 39 IndexRangeCollection::IndexRangeCollection(const QString &index_ranges_string,
103 Enums::LocationType location_type,
104
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 QObject *parent)
105 {
106 39 QList<IndexRange *> index_ranges = IndexRangeCollection::parseIndexRanges(
107
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 index_ranges_string, location_type, parent);
108
109
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 setIndexRanges(index_ranges);
110
111 39 qDebug() << "With text" << index_ranges_string
112 << "initialized this:" << indicesAsText();
113 39 }
114
115 /*!
116 \brief Constructs an IndexRangeCollection as a copy of \a other.
117 */
118 10509 IndexRangeCollection::IndexRangeCollection(const IndexRangeCollection &other,
119 10509 QObject *parent)
120
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 10503 times.
10509 : QObject(parent), m_comment(other.m_comment)
121 {
122 10509 qDebug() << "Pseudo copy constructing IndexRangeCollection"
123 << other.indicesAsText();
124
3/4
✓ Branch 2 taken 10665 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10665 times.
✓ Branch 5 taken 10509 times.
21174 foreach(const IndexRange *item, other.m_ranges)
125
3/8
✓ Branch 1 taken 10665 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10665 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10665 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
10665 m_ranges.append(new IndexRange(item->m_start, item->m_stop, this));
126 10509 qDebug() << "After copying IndexRangeCollection, this:"
127 << this->indicesAsText();
128 10509 }
129
130 /*!
131 \brief Destructs this IndexRangeCollection instance.
132
133 The \l IndexRangeCollection instances are freed.
134 */
135 48888 IndexRangeCollection::~IndexRangeCollection()
136 {
137 48888 }
138
139 /*!
140 \brief Initializes this IndexRangeCollection instance using \a other and returns
141 a reference to this instance.
142 */
143 IndexRangeCollection &
144 3 IndexRangeCollection::initialize(const IndexRangeCollection &other)
145 {
146 3 m_comment = other.m_comment;
147
148 3 qDeleteAll(m_ranges);
149 3 m_ranges.clear();
150
151
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 foreach(const IndexRange *item, m_ranges)
152 m_ranges.append(new IndexRange(*item, this));
153
154 3 return *this;
155 }
156
157 /*!
158 \brief Returns a newly allocated IndexRangeCollection instance with parent set
159 to \a parent.
160 */
161 IndexRangeCollection *
162 IndexRangeCollection::clone(QObject *parent)
163 {
164 IndexRangeCollection *copy_p = new IndexRangeCollection(parent);
165
166 foreach(const IndexRange *item, m_ranges)
167 copy_p->m_ranges.append(new IndexRange(*item, copy_p));
168
169 return copy_p;
170 }
171
172 /*!
173 \ b*rief Returns a newly allocated IndexRangeCollection instance initialized
174 using \a other and with parent set to \a parent.
175 */
176 IndexRangeCollection *
177 IndexRangeCollection::clone(const IndexRangeCollection &other, QObject *parent)
178 {
179 IndexRangeCollection *copy_p = new IndexRangeCollection(parent);
180
181 foreach(const IndexRange *item, other.m_ranges)
182 copy_p->m_ranges.append(new IndexRange(*item, copy_p));
183
184 return copy_p;
185 }
186
187 /*!
188 \brief Sets the comment to \a comment.
189 */
190 void
191 48 IndexRangeCollection::setComment(const QString &comment)
192 {
193 48 m_comment = comment;
194 48 }
195
196 /*!
197 \brief Returns the comment.
198 */
199 QString
200 51 IndexRangeCollection::getComment() const
201 {
202
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 return m_comment;
203 }
204
205 /*!
206 \brief Returns a const reference to the IndexRange container.
207 */
208 const QList<IndexRange *> &
209 16332 IndexRangeCollection::getRangesCstRef() const
210 {
211 16332 return m_ranges;
212 }
213
214 /*!
215 \brief Returns a reference to the IndexRange container.
216 */
217 QList<IndexRange *> &
218 IndexRangeCollection::getRangesRef()
219 {
220 return m_ranges;
221 }
222
223 /*!
224 \brief Parses \a index_ranges_string and returns a container with all the
225 IndexRange entities parsed and parentship set to \a parent.
226
227 If \a location_type is Enums::LocationType::POSITIION, the parsed values are
228 decremented by one unit to convert from Position to Index. The values stored in
229 the \c IndexRangeCollection are always indices.
230
231
232 If \a location_type is Enums::LocationType::INDEX, the parsed values are stored
233 as is.
234
235 The \a index_ranges_string might contain one or more substrings in
236 the format "[15-220]", like "[0-20][0-30][10-50][15-80][25-100][30-100]".
237
238 If an error occurs, an empty container is returned.
239
240 A sanity check is performed: the counts of '[' and of ']' must be the same. At
241 the stop of the parse, the size of IndexRangeCollection must be same as the
242 count of
243 '['.
244
245 If that check fails, an empty container is returned.
246 */
247 QList<IndexRange *>
248 87 IndexRangeCollection::parseIndexRanges(const QString &index_ranges_string,
249 Enums::LocationType location_type,
250 QObject *parent)
251 {
252 87 qDebug() << "Parsing text:" << index_ranges_string;
253
254
1/2
✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
87 QString local_index_ranges_string = index_ranges_string;
255
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
87 local_index_ranges_string = Utils::unspacify(local_index_ranges_string);
256
257 // Sanity check
258
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
87 qsizetype opening_brackets_count = local_index_ranges_string.count('[');
259
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
87 qsizetype closing_brackets_count = local_index_ranges_string.count(']');
260
261
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 81 times.
87 if(opening_brackets_count != closing_brackets_count)
262 {
263
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 qCritical() << "The string does not represent bona fide IndexRange "
264
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 "descriptions.";
265
266 6 return QList<IndexRange *>();
267 }
268
269
2/4
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 81 times.
✗ Branch 5 not taken.
81 QRegularExpression sub_regexp("\\[(\\d+)-(\\d+)\\]");
270
271 81 bool ok = false;
272 81 QList<IndexRange *> parsed_index_ranges;
273
274
1/2
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
81 for(const QRegularExpressionMatch &match :
275
4/6
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 81 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 234 times.
✓ Branch 7 taken 81 times.
396 sub_regexp.globalMatch(local_index_ranges_string))
276 {
277
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 QString sub_match = match.captured(0);
278
279 234 qDebug() << "sub_match all captured" << sub_match;
280
281
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 QString start_string = match.captured(1);
282
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 QString stop_string = match.captured(2);
283
284
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 int start = start_string.toInt(&ok);
285
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if(!ok)
287 qFatalStream() << "Failed to parse the IndexRange instance(s)";
288
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 int stop = stop_string.toInt(&ok);
289
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if(!ok)
291 qFatalStream() << "Failed to parse the IndexRange instance(s)";
292
293
2/2
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 84 times.
234 if(location_type == Enums::LocationType::POSITION)
294 {
295 // Convert from input positions to local indices
296 150 --start;
297 150 --stop;
298 }
299
300 234 qDebug() << "start and stop:" << start << "and" << stop;
301
302 // << "These values will be sorted in ascending order upon IndexRange
303 // construction";
304
3/8
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 234 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 234 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
234 parsed_index_ranges.append(new IndexRange(start, stop, parent));
305
1/2
✓ Branch 3 taken 234 times.
✗ Branch 4 not taken.
315 }
306
307 // Sanity check
308
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 75 times.
81 if(parsed_index_ranges.size() != opening_brackets_count)
309 {
310
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 qWarning() << "The string does not represent bona fide IndexRange "
311
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 "descriptions.";
312 6 parsed_index_ranges.clear();
313 }
314
315 81 qDebug() << "Could parse" << parsed_index_ranges.size() << "index ranges";
316
317 81 return parsed_index_ranges;
318 168 }
319
320 //////////////// OPERATORS /////////////////////
321
322 /*!
323 \brief Returns true if this IndexRangeCollection instance is identical to \a
324 other, false otherwise.
325 */
326 bool
327 15 IndexRangeCollection::operator==(const IndexRangeCollection &other) const
328 {
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if(m_comment != other.m_comment)
330 return false;
331
332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if(m_ranges.size() != other.m_ranges.size())
333 return false;
334
335
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 9 times.
39 for(qsizetype iter = 0; iter < m_ranges.size(); ++iter)
336
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 24 times.
30 if(*m_ranges.at(iter) != *other.m_ranges.at(iter))
337 return false;
338
339 return true;
340 }
341
342 /*!
343 \brief Returns true if this IndexRangeCollection is different than \a other,
344 false otherwise.
345
346 This funtion returns the negated result of operator==().
347 */
348 bool
349 6 IndexRangeCollection::operator!=(const IndexRangeCollection &other) const
350 {
351 6 return !operator==(other);
352 }
353
354 /*!
355 \brief Clears this IndexRangeCollection's container of IndexRange instances and
356 adds one IndexRange using \a start and \a stop.
357 */
358 void
359 6 IndexRangeCollection::setIndexRange(qsizetype start, qsizetype stop)
360 {
361 6 qDeleteAll(m_ranges);
362 6 m_ranges.clear();
363
364
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 m_ranges.push_back(new IndexRange(start, stop, this));
365 6 }
366
367 /*!
368 \brief Clears this IndexRangeCollection's container of IndexRange instances and
369 adds one \a index_range IndexRange.
370 */
371 void
372 2403 IndexRangeCollection::setIndexRange(const IndexRange &index_range)
373 {
374 2403 qDeleteAll(m_ranges);
375 2403 m_ranges.clear();
376
377
1/2
✓ Branch 2 taken 2403 times.
✗ Branch 3 not taken.
2403 m_ranges.push_back(new IndexRange(index_range, this));
378 2403 }
379
380 /*!
381 \brief Clears this IndexRangeCollection's container of IndexRange instances and
382 adds the IndexRange instances contained in the \a index_ranges container.
383 */
384 void
385 153 IndexRangeCollection::setIndexRanges(const QList<IndexRange *> &index_ranges)
386 {
387 153 qDeleteAll(m_ranges);
388 153 m_ranges.clear();
389
390
2/2
✓ Branch 1 taken 309 times.
✓ Branch 2 taken 153 times.
462 foreach(const IndexRange *item, index_ranges)
391
3/8
✓ Branch 1 taken 309 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 309 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 309 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
309 m_ranges.append(new IndexRange(*item, this));
392 153 }
393
394 /*!
395 \brief Clears this IndexRangeCollection's container of IndexRange and adds the
396 IndexRange instances contained in the \a index_ranges collection.
397 */
398 void
399 27 IndexRangeCollection::setIndexRanges(const IndexRangeCollection &index_ranges)
400 {
401 27 qDeleteAll(m_ranges);
402 27 m_ranges.clear();
403
404
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 27 times.
105 foreach(const IndexRange *item, index_ranges.m_ranges)
405
3/8
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 78 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
78 m_ranges.append(new IndexRange(*item, this));
406 27 }
407
408 /*!
409 \brief Creates the IndexRange instances based on \a index_ranges_string
410 and adds them to this IndexRange container.
411
412 \note The container of IndexRange instances is first emptied.
413
414 If there are not multiple regions, the format of the \a index_ranges_string
415 is:
416
417 \code
418 "[228-246]"
419 \endcode
420
421 If there are multiple regions (for example
422 when a cross-link exists), the format changes to account for the multiple
423 regions:
424
425 \code
426 "[228-246][276-282][247-275]"
427 \endcode
428
429 \note It is expected that the values in the index_ranges_string are \e
430 positions strings and not \e indices.
431
432 Returns the count of added IndexRange instances or -1 if an error occurred.
433 */
434 qsizetype
435 48 IndexRangeCollection::setIndexRanges(const QString &index_ranges_string,
436 Enums::LocationType location_type)
437 {
438 // We get a string in the form [xxx-yyy] (there can be more than
439 // one such element, if cross-linked oligomers are calculated, like
440 // "[228-246][276-282][247-275]". Because that string comes from
441 // outside code, it is expected to contains positions
442 // and not indices. So we have to decrement the start and stop
443 // values by one.
444
445 // qDebug() << "index_ranges_string:" << index_ranges_string;
446
447 48 QList<IndexRange *> index_ranges =
448 48 parseIndexRanges(index_ranges_string, location_type);
449
450
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 setIndexRanges(index_ranges);
451
452
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 return size();
453 48 }
454
455 /*!
456 \brief Adds one IndexRange using \a start and \a stop.
457 */
458 void
459 IndexRangeCollection::appendIndexRange(qsizetype start, qsizetype stop)
460 {
461 m_ranges.append(new IndexRange(start, stop, this));
462 }
463
464 /*!
465 \brief Adds one IndexRange as a copy of \a index_range.
466 */
467 void
468 132 IndexRangeCollection::appendIndexRange(const IndexRange &index_range)
469 {
470
1/2
✓ Branch 2 taken 132 times.
✗ Branch 3 not taken.
132 m_ranges.append(new IndexRange(index_range, this));
471 132 }
472
473 /*!
474 \brief Adds IndexRange instances as copies of the instances in \a
475 index_ranges.
476 */
477 void
478 IndexRangeCollection::appendIndexRanges(const QList<IndexRange *> &index_ranges)
479 {
480 foreach(const IndexRange *item, index_ranges)
481 m_ranges.append(new IndexRange(*item, this));
482 }
483
484 /*!
485 \brief Adds IndexRange instances as copies of the instances in \a
486 index_ranges.
487 */
488 void
489 66 IndexRangeCollection::appendIndexRanges(
490 const IndexRangeCollection &index_ranges)
491 {
492
2/2
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 66 times.
141 foreach(const IndexRange *item, index_ranges.m_ranges)
493
3/8
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 75 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
75 m_ranges.append(new IndexRange(*item, this));
494 66 }
495
496 //////////////// ACCESSING FUNCTIONS /////////////////////
497 /*!
498 \brief Returns a const reference to the IndexRange at \a index.
499
500 An index that is out of bounds is fatal.
501 */
502 const IndexRange &
503 414 IndexRangeCollection::getRangeCstRefAt(qsizetype index) const
504 {
505
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 414 times.
414 if(index >= size())
506 qFatalStream() << "Programming error. Index is out of bounds.";
507
508 414 return *m_ranges.at(index);
509 }
510
511 /*!
512 \brief Returns reference to the IndexRange at \a index.
513
514 An index that is out of bounds is fatal.
515 */
516 IndexRange &
517 6 IndexRangeCollection::getRangeRefAt(qsizetype index)
518 {
519
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if(index >= size())
520 qFatalStream() << "Programming error. Index is out of bounds.";
521
522 6 return *m_ranges.at(index);
523 }
524
525 /*!
526 \brief Returns reference to the IndexRange at \a index.
527
528 An index that is out of bounds is fatal.
529 */
530 IndexRange *
531 IndexRangeCollection::getRangeAt(qsizetype index)
532 {
533 if(index < 0 || index >= size())
534 qFatalStream() << "Programming error. Index is out of bounds.";
535
536 return m_ranges.at(index);
537 }
538
539 /*!
540 \brief Returns a constant iterator to the IndexRange at \a index.
541
542 An index that is out of bounds is fatal.
543 */
544 const QList<IndexRange *>::const_iterator
545 IndexRangeCollection::getRangeCstIteratorAt(qsizetype index) const
546 {
547 if(index < 0 || index >= size())
548 qFatalStream() << "Programming error. Index is out of bounds.";
549
550 return m_ranges.cbegin() + index;
551 }
552
553 /*!
554 \brief Returns an iterator to the IndexRange at \a index.
555
556 An index that is out of bounds is fatal.
557 */
558 const QList<IndexRange *>::iterator
559 IndexRangeCollection::getRangeIteratorAt(qsizetype index)
560 {
561 if(index < 0 || index >= size())
562 qFatalStream() << "Programming error. Index is out of bounds.";
563
564 return m_ranges.begin() + index;
565 }
566
567 /*!
568 \brief Returns the left-most start value throughout all the IndexRange
569 instances.
570 */
571 qsizetype
572 78 IndexRangeCollection::leftMostIndexRangeStart() const
573 {
574 78 qDebug() << "IndexRangeCollection:" << this->indicesAsText();
575
576 78 qsizetype left_most_value = std::numeric_limits<qsizetype>::max();
577
578
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 78 times.
183 foreach(const IndexRange *item, m_ranges)
579 {
580 105 if(item->m_start < left_most_value)
581 left_most_value = item->m_start;
582 78 }
583
584 78 return left_most_value;
585 }
586
587 /*!
588 \brief Returns a container with the indices of the IndexRange instances that
589 have the smallest start value.
590
591 Searches all the IndexRange instances in this IndexRangeCollection instance that
592 share the same IndexRange::start value that is actually the smallest such start
593 value in the container. Each found IndexRange instance's index in this
594 IndexRangeCollection instance is added to the container.
595
596 \sa indicesOfRightMostIndexRanges(), isLeftMostIndexRange(),
597 rightMostIndexRangeStop()
598 */
599 QList<qsizetype>
600 9 IndexRangeCollection::indicesOfLeftMostIndexRanges() const
601 {
602 9 QList<qsizetype> indices;
603
604
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 if(!size())
605 return indices;
606
607
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 qsizetype left_most_value = leftMostIndexRangeStart();
608
609 // At this point we know what's the leftmost index. We can use that
610 // index to search for all the items that are also leftmost.
611
612
2/3
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
39 for(qsizetype iter = 0; iter < m_ranges.size(); ++iter)
613 {
614
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
30 if(m_ranges.at(iter)->m_start == left_most_value)
615
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 indices.push_back(iter);
616 }
617
618 return indices;
619 }
620
621 /*!
622 \brief Returns true if \a index_range is the left-most IndexRange
623 instance in this IndexRangeCollection instance, false otherwise.
624 */
625 bool
626 30 IndexRangeCollection::isLeftMostIndexRange(const IndexRange &index_range) const
627 {
628 // Is index_range the leftmost sequence range of *this
629 // IndexRangeCollection instance ?
630
631 30 qsizetype value = index_range.m_start;
632
633
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 12 times.
78 foreach(const IndexRange *item, m_ranges)
634 {
635
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 48 times.
66 if(item->m_start < value)
636 18 return false;
637 18 }
638
639 12 return true;
640 }
641
642 /*!
643 \brief Return the right most stop ordinate throughout all the IndexRanges
644 instances.
645 */
646 qsizetype
647 78 IndexRangeCollection::rightMostIndexRangeStop() const
648 {
649 78 qDebug() << "IndexRangeCollection:" << this->indicesAsText();
650
651 78 qsizetype rightMostValue = std::numeric_limits<qsizetype>::min();
652
653
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 78 times.
183 foreach(const IndexRange *item, m_ranges)
654 {
655 105 if(item->m_stop > rightMostValue)
656 rightMostValue = item->m_stop;
657 78 }
658
659 78 return rightMostValue;
660 }
661
662 /*!
663 \brief Returns a container with the indices of the IndexRange instances that
664 have the greater stop value.
665
666 Searches all the IndexRange instances in this IndexRangeCollection instance that
667 share the same IndexRange::stop value that is actually the greatest such stopt
668 value in the container. Each found IndexRange instance's index in this
669 IndexRangeCollection instance is added to the container.
670 \sa indicesOfLeftMostIndexRanges(), isLeftMostIndexRange(),
671 isRightMostIndexRange()
672 */
673 QList<qsizetype>
674 9 IndexRangeCollection::indicesOfRightMostIndexRanges() const
675 {
676 9 QList<qsizetype> indices;
677
678
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 if(!size())
679 return indices;
680
681
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 qsizetype right_most_value = rightMostIndexRangeStop();
682
683 // At this point we now what's the rightmost index. We can use
684 // that index to search for all the items that are also
685 // rightmost.
686
687
2/3
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
39 for(qsizetype iter = 0; iter < m_ranges.size(); ++iter)
688 {
689
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 18 times.
30 if(m_ranges.at(iter)->m_stop == right_most_value)
690
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 indices.push_back(iter);
691 }
692
693 return indices;
694 }
695
696 /*!
697 \brief Returns true if \a index_range is the right-most IndexRange
698 instance in this IndexRangeCollection instance, false otherwise.
699 */
700 bool
701 30 IndexRangeCollection::isRightMostIndexRange(const IndexRange &index_range) const
702 {
703 // Is index_range the rightmost sequence range of *this
704 // IndexRangeCollection instance ?
705
706 30 qsizetype value = index_range.m_stop;
707
708
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 12 times.
117 foreach(const IndexRange *item, m_ranges)
709 {
710
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 87 times.
105 if(item->m_stop > value)
711 18 return false;
712 18 }
713
714 12 return true;
715 }
716
717 /*!
718 \brief Returns an \l IndexRange containing the leftmost start index and the
719 rightmost stop index, effectively providing the most inclusive index range.
720 */
721 IndexRange *
722 66 IndexRangeCollection::mostInclusiveLeftRightIndexRange() const
723 {
724 66 return new IndexRange(leftMostIndexRangeStart(),
725 rightMostIndexRangeStop(),
726
3/7
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 66 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 66 times.
✗ Branch 9 not taken.
66 const_cast<IndexRangeCollection *>(this));
727 }
728
729 /*!
730 \brief Returns true if \a index is found to be between the start and stop
731 indices of at least one IndexRange instance in this IndexRangeCollection
732 instance, false otherwise.
733
734 If \a globally is set to true, then returns true if \a index is contained
735 in the interval [smallest - greatest] indices throughout all the IndexRange
736 instances.
737
738 \sa leftMostIndexRangeStart(), rightMostIndexRangeStop()
739 */
740 bool
741 10101 IndexRangeCollection::encompassIndex(qsizetype index, bool globally) const
742 {
743
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10098 times.
10101 if(globally)
744
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
6 return (index >= leftMostIndexRangeStart() &&
745 3 index <= rightMostIndexRangeStop());
746
747
2/2
✓ Branch 1 taken 10158 times.
✓ Branch 2 taken 9948 times.
20106 foreach(const IndexRange *item, m_ranges)
748 {
749
4/4
✓ Branch 0 taken 8124 times.
✓ Branch 1 taken 2034 times.
✓ Branch 2 taken 150 times.
✓ Branch 3 taken 7974 times.
10158 if(item->m_start <= index && item->m_stop >= index)
750 150 return true;
751 150 }
752
753 9948 return false;
754 }
755
756 /*!
757 \brief Returns true if at least two IndexRange instances overlap.
758
759 Two IndexRange instances overlap if the second's start value is less than
760 the first's stop value and greater than the first's start value:
761
762 |-----------------|
763 |=================|
764
765 or
766
767 |-----------------|
768 |=================|
769
770 \sa encompassIndex()
771 */
772 bool
773 9 IndexRangeCollection::overlap() const
774 {
775 // Return true if there are overlapping regions in this
776 // IndexRangeCollection instance.
777
778
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
9 if(size() < 2)
779 return false;
780
781
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3 times.
15 foreach(const IndexRange *first_item, m_ranges)
782 {
783 12 qsizetype first_1 = first_item->m_start;
784 12 qsizetype second_1 = first_item->m_stop;
785
786
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 9 times.
42 foreach(const IndexRange *second_item, m_ranges)
787 {
788
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 21 times.
33 if(second_item == first_item)
789 12 continue;
790
791 21 qsizetype first_2 = second_item->m_start;
792
793
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 18 times.
21 if(first_2 <= second_1 && first_2 >= first_1)
794 3 return true;
795 12 }
796 3 }
797
798 3 return false;
799 }
800
801 /*!
802 \brief Returns a string documenting the IndexRange instances in this
803 IndexRanges instance.
804
805 Each IndexRange instance is described like the following, with the values
806 being the start and stop index values in the IndexRange:
807
808 \code
809 [156-350]
810 \endcode
811
812 \sa positionsAsText()
813 */
814 QString
815 3 IndexRangeCollection::indicesAsText() const
816 {
817 3 QString text;
818
819
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
12 foreach(const IndexRange *item, m_ranges)
820 {
821
3/6
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
18 text += QString("[%1-%2]").arg(item->m_start).arg(item->m_stop);
822 }
823
824 3 return text;
825 }
826
827 /*!
828 \brief Returns a string documenting the IndexRangeCollection in this
829 IndexRanges.
830
831 Each IndexRange instance is described like the following with the values
832 being the start and stop position values in the IndexRange (start + 1, stop
833 +1):
834
835 \code
836 [157-351]
837 \endcode
838
839 \note The values reported are not \e indices, but \e positions.
840
841 \sa indicesAsText()
842 */
843 QString
844 132 IndexRangeCollection::positionsAsText() const
845 {
846 132 QString text;
847
848
2/2
✓ Branch 1 taken 150 times.
✓ Branch 2 taken 132 times.
282 foreach(const IndexRange *item, m_ranges)
849 {
850
3/6
✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 150 times.
✗ Branch 8 not taken.
300 text += QString("[%1-%2]").arg(item->m_start + 1).arg(item->m_stop + 1);
851 }
852
853 132 return text;
854 }
855
856 /*!
857 \brief Returns the size of the container of IndexRange instances.
858 */
859 qsizetype
860 5691 IndexRangeCollection::size() const
861 {
862
0/16
✗ 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 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
5691 return m_ranges.size();
863 }
864
865 /*!
866 \brief Clears all the members of this IndexRangeCollection instance.
867 */
868 void
869 12 IndexRangeCollection::clear()
870 {
871 12 m_comment = "";
872 12 m_ranges.clear();
873 12 }
874
875 void
876 IndexRangeCollection::registerJsConstructor(QJSEngine *engine)
877
878 {
879 if(!engine)
880 {
881 qWarning()
882 << "Cannot register IndexRangeCollection class: engine is null";
883 return;
884 }
885
886 // Register the meta object as a constructor
887
888 QJSValue jsMetaObject =
889 engine->newQMetaObject(&IndexRangeCollection::staticMetaObject);
890 engine->globalObject().setProperty("IndexRangeCollection", jsMetaObject);
891 }
892
893
894 } // namespace libXpertMassCore
895 } // namespace MsXpS
896