GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/IndexRangeCollection.cpp
Date: 2025-11-20 01:41:33
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 258 IndexRangeCollection::IndexRangeCollection(QObject *parent): QObject(parent)
80 {
81 258 }
82
83 /*!
84 \brief Constructs an IndexRangeCollection instance with a single IndexRange
85 object using \a index_start and \a index_stop.
86 */
87 711 IndexRangeCollection::IndexRangeCollection(qsizetype index_start,
88 qsizetype index_stop,
89 711 QObject *parent)
90
1/2
✓ Branch 2 taken 711 times.
✗ Branch 3 not taken.
711 : QObject(parent)
91 {
92
2/4
✓ Branch 1 taken 711 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 711 times.
✗ Branch 5 not taken.
711 IndexRange *index_range_p = new IndexRange(index_start, index_stop, this);
93
94
1/2
✓ Branch 1 taken 711 times.
✗ Branch 2 not taken.
711 m_ranges.append(index_range_p);
95 711 }
96
97 /*!
98 \brief Constructs an IndexRangeCollection instance using \a index_ranges_string
99
100 \sa parseIndexRanges()
101 */
102 13 IndexRangeCollection::IndexRangeCollection(const QString &index_ranges_string,
103 Enums::LocationType location_type,
104
1/2
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 QObject *parent)
105 {
106 13 QList<IndexRange *> index_ranges = IndexRangeCollection::parseIndexRanges(
107
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 index_ranges_string, location_type, parent);
108
109
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 setIndexRanges(index_ranges);
110
111 13 qDebug() << "With text" << index_ranges_string
112 << "initialized this:" << indicesAsText();
113 13 }
114
115 /*!
116 \brief Constructs an IndexRangeCollection as a copy of \a other.
117 */
118 3503 IndexRangeCollection::IndexRangeCollection(const IndexRangeCollection &other,
119 3503 QObject *parent)
120
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3501 times.
3503 : QObject(parent), m_comment(other.m_comment)
121 {
122 3503 qDebug() << "Pseudo copy constructing IndexRangeCollection"
123 << other.indicesAsText();
124
3/4
✓ Branch 2 taken 3555 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3555 times.
✓ Branch 5 taken 3503 times.
7058 foreach(const IndexRange *item, other.m_ranges)
125
3/8
✓ Branch 1 taken 3555 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3555 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3555 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
3555 m_ranges.append(new IndexRange(item->m_start, item->m_stop, this));
126 3503 qDebug() << "After copying IndexRangeCollection, this:"
127 << this->indicesAsText();
128 3503 }
129
130 /*!
131 \brief Destructs this IndexRangeCollection instance.
132
133 The \l IndexRangeCollection instances are freed.
134 */
135 16296 IndexRangeCollection::~IndexRangeCollection()
136 {
137 16296 }
138
139 /*!
140 \brief Initializes this IndexRangeCollection instance using \a other and returns
141 a reference to this instance.
142 */
143 IndexRangeCollection &
144 1 IndexRangeCollection::initialize(const IndexRangeCollection &other)
145 {
146 1 m_comment = other.m_comment;
147
148 1 qDeleteAll(m_ranges);
149 1 m_ranges.clear();
150
151
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 foreach(const IndexRange *item, m_ranges)
152 m_ranges.append(new IndexRange(*item, this));
153
154 1 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 16 IndexRangeCollection::setComment(const QString &comment)
192 {
193 16 m_comment = comment;
194 16 }
195
196 /*!
197 \brief Returns the comment.
198 */
199 QString
200 17 IndexRangeCollection::getComment() const
201 {
202
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 return m_comment;
203 }
204
205 /*!
206 \brief Returns a const reference to the IndexRange container.
207 */
208 const QList<IndexRange *> &
209 5444 IndexRangeCollection::getRangesCstRef() const
210 {
211 5444 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 29 IndexRangeCollection::parseIndexRanges(const QString &index_ranges_string,
249 Enums::LocationType location_type,
250 QObject *parent)
251 {
252 29 qDebug() << "Parsing text:" << index_ranges_string;
253
254
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 QString local_index_ranges_string = index_ranges_string;
255
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 local_index_ranges_string = Utils::unspacify(local_index_ranges_string);
256
257 // Sanity check
258
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 qsizetype opening_brackets_count = local_index_ranges_string.count('[');
259
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 qsizetype closing_brackets_count = local_index_ranges_string.count(']');
260
261
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 27 times.
29 if(opening_brackets_count != closing_brackets_count)
262 {
263
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 qCritical() << "The string does not represent bona fide IndexRange "
264
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 "descriptions.";
265
266 2 return QList<IndexRange *>();
267 }
268
269
2/4
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
27 QRegularExpression sub_regexp("\\[(\\d+)-(\\d+)\\]");
270
271 27 bool ok = false;
272 27 QList<IndexRange *> parsed_index_ranges;
273
274
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 for(const QRegularExpressionMatch &match :
275
4/6
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 78 times.
✓ Branch 7 taken 27 times.
132 sub_regexp.globalMatch(local_index_ranges_string))
276 {
277
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 QString sub_match = match.captured(0);
278
279 78 qDebug() << "sub_match all captured" << sub_match;
280
281
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 QString start_string = match.captured(1);
282
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 QString stop_string = match.captured(2);
283
284
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 int start = start_string.toInt(&ok);
285
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if(!ok)
287 qFatalStream() << "Failed to parse the IndexRange instance(s)";
288
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 int stop = stop_string.toInt(&ok);
289
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if(!ok)
291 qFatalStream() << "Failed to parse the IndexRange instance(s)";
292
293
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 28 times.
78 if(location_type == Enums::LocationType::POSITION)
294 {
295 // Convert from input positions to local indices
296 50 --start;
297 50 --stop;
298 }
299
300 78 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 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 10 not taken.
✗ Branch 11 not taken.
78 parsed_index_ranges.append(new IndexRange(start, stop, parent));
305
1/2
✓ Branch 3 taken 78 times.
✗ Branch 4 not taken.
105 }
306
307 // Sanity check
308
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 25 times.
27 if(parsed_index_ranges.size() != opening_brackets_count)
309 {
310
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 qWarning() << "The string does not represent bona fide IndexRange "
311
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 "descriptions.";
312 2 parsed_index_ranges.clear();
313 }
314
315 27 qDebug() << "Could parse" << parsed_index_ranges.size() << "index ranges";
316
317 27 return parsed_index_ranges;
318 56 }
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 5 IndexRangeCollection::operator==(const IndexRangeCollection &other) const
328 {
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if(m_comment != other.m_comment)
330 return false;
331
332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if(m_ranges.size() != other.m_ranges.size())
333 return false;
334
335
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
13 for(qsizetype iter = 0; iter < m_ranges.size(); ++iter)
336
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
10 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 2 IndexRangeCollection::operator!=(const IndexRangeCollection &other) const
350 {
351 2 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 2 IndexRangeCollection::setIndexRange(qsizetype start, qsizetype stop)
360 {
361 2 qDeleteAll(m_ranges);
362 2 m_ranges.clear();
363
364
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 m_ranges.push_back(new IndexRange(start, stop, this));
365 2 }
366
367 /*!
368 \brief Clears this IndexRangeCollection's container of IndexRange instances and
369 adds one \a index_range IndexRange.
370 */
371 void
372 801 IndexRangeCollection::setIndexRange(const IndexRange &index_range)
373 {
374 801 qDeleteAll(m_ranges);
375 801 m_ranges.clear();
376
377
1/2
✓ Branch 2 taken 801 times.
✗ Branch 3 not taken.
801 m_ranges.push_back(new IndexRange(index_range, this));
378 801 }
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 51 IndexRangeCollection::setIndexRanges(const QList<IndexRange *> &index_ranges)
386 {
387 51 qDeleteAll(m_ranges);
388 51 m_ranges.clear();
389
390
2/2
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 51 times.
154 foreach(const IndexRange *item, index_ranges)
391
3/8
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 103 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 103 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
103 m_ranges.append(new IndexRange(*item, this));
392 51 }
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 9 IndexRangeCollection::setIndexRanges(const IndexRangeCollection &index_ranges)
400 {
401 9 qDeleteAll(m_ranges);
402 9 m_ranges.clear();
403
404
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 9 times.
35 foreach(const IndexRange *item, index_ranges.m_ranges)
405
3/8
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 26 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
26 m_ranges.append(new IndexRange(*item, this));
406 9 }
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 16 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 16 QList<IndexRange *> index_ranges =
448 16 parseIndexRanges(index_ranges_string, location_type);
449
450
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 setIndexRanges(index_ranges);
451
452
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 return size();
453 16 }
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 44 IndexRangeCollection::appendIndexRange(const IndexRange &index_range)
469 {
470
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 m_ranges.append(new IndexRange(index_range, this));
471 44 }
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 22 IndexRangeCollection::appendIndexRanges(
490 const IndexRangeCollection &index_ranges)
491 {
492
2/2
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 22 times.
47 foreach(const IndexRange *item, index_ranges.m_ranges)
493
3/8
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
25 m_ranges.append(new IndexRange(*item, this));
494 22 }
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 138 IndexRangeCollection::getRangeCstRefAt(qsizetype index) const
504 {
505
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 138 times.
138 if(index >= size())
506 qFatalStream() << "Programming error. Index is out of bounds.";
507
508 138 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 2 IndexRangeCollection::getRangeRefAt(qsizetype index)
518 {
519
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(index >= size())
520 qFatalStream() << "Programming error. Index is out of bounds.";
521
522 2 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 26 IndexRangeCollection::leftMostIndexRangeStart() const
573 {
574 26 qDebug() << "IndexRangeCollection:" << this->indicesAsText();
575
576 26 qsizetype left_most_value = std::numeric_limits<qsizetype>::max();
577
578
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 26 times.
61 foreach(const IndexRange *item, m_ranges)
579 {
580 35 if(item->m_start < left_most_value)
581 left_most_value = item->m_start;
582 26 }
583
584 26 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 3 IndexRangeCollection::indicesOfLeftMostIndexRanges() const
601 {
602 3 QList<qsizetype> indices;
603
604
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 if(!size())
605 return indices;
606
607
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 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 10 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
13 for(qsizetype iter = 0; iter < m_ranges.size(); ++iter)
613 {
614
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 if(m_ranges.at(iter)->m_start == left_most_value)
615
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 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 10 IndexRangeCollection::isLeftMostIndexRange(const IndexRange &index_range) const
627 {
628 // Is index_range the leftmost sequence range of *this
629 // IndexRangeCollection instance ?
630
631 10 qsizetype value = index_range.m_start;
632
633
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 4 times.
26 foreach(const IndexRange *item, m_ranges)
634 {
635
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 16 times.
22 if(item->m_start < value)
636 6 return false;
637 6 }
638
639 4 return true;
640 }
641
642 /*!
643 \brief Return the right most stop ordinate throughout all the IndexRanges
644 instances.
645 */
646 qsizetype
647 26 IndexRangeCollection::rightMostIndexRangeStop() const
648 {
649 26 qDebug() << "IndexRangeCollection:" << this->indicesAsText();
650
651 26 qsizetype rightMostValue = std::numeric_limits<qsizetype>::min();
652
653
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 26 times.
61 foreach(const IndexRange *item, m_ranges)
654 {
655 35 if(item->m_stop > rightMostValue)
656 rightMostValue = item->m_stop;
657 26 }
658
659 26 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 3 IndexRangeCollection::indicesOfRightMostIndexRanges() const
675 {
676 3 QList<qsizetype> indices;
677
678
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 if(!size())
679 return indices;
680
681
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 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 10 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
13 for(qsizetype iter = 0; iter < m_ranges.size(); ++iter)
688 {
689
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 if(m_ranges.at(iter)->m_stop == right_most_value)
690
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 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 10 IndexRangeCollection::isRightMostIndexRange(const IndexRange &index_range) const
702 {
703 // Is index_range the rightmost sequence range of *this
704 // IndexRangeCollection instance ?
705
706 10 qsizetype value = index_range.m_stop;
707
708
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 4 times.
39 foreach(const IndexRange *item, m_ranges)
709 {
710
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 29 times.
35 if(item->m_stop > value)
711 6 return false;
712 6 }
713
714 4 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 22 IndexRangeCollection::mostInclusiveLeftRightIndexRange() const
723 {
724 22 return new IndexRange(leftMostIndexRangeStart(),
725 rightMostIndexRangeStop(),
726
3/7
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 22 times.
✗ Branch 9 not taken.
22 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 3367 IndexRangeCollection::encompassIndex(qsizetype index, bool globally) const
742 {
743
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3366 times.
3367 if(globally)
744
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
2 return (index >= leftMostIndexRangeStart() &&
745 1 index <= rightMostIndexRangeStop());
746
747
2/2
✓ Branch 1 taken 3386 times.
✓ Branch 2 taken 3316 times.
6702 foreach(const IndexRange *item, m_ranges)
748 {
749
4/4
✓ Branch 0 taken 2708 times.
✓ Branch 1 taken 678 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 2658 times.
3386 if(item->m_start <= index && item->m_stop >= index)
750 50 return true;
751 50 }
752
753 3316 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 3 IndexRangeCollection::overlap() const
774 {
775 // Return true if there are overlapping regions in this
776 // IndexRangeCollection instance.
777
778
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if(size() < 2)
779 return false;
780
781
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
5 foreach(const IndexRange *first_item, m_ranges)
782 {
783 4 qsizetype first_1 = first_item->m_start;
784 4 qsizetype second_1 = first_item->m_stop;
785
786
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 3 times.
14 foreach(const IndexRange *second_item, m_ranges)
787 {
788
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
11 if(second_item == first_item)
789 4 continue;
790
791 7 qsizetype first_2 = second_item->m_start;
792
793
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if(first_2 <= second_1 && first_2 >= first_1)
794 1 return true;
795 4 }
796 1 }
797
798 1 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 1 IndexRangeCollection::indicesAsText() const
816 {
817 1 QString text;
818
819
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
4 foreach(const IndexRange *item, m_ranges)
820 {
821
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 text += QString("[%1-%2]").arg(item->m_start).arg(item->m_stop);
822 }
823
824 1 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 44 IndexRangeCollection::positionsAsText() const
845 {
846 44 QString text;
847
848
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 44 times.
94 foreach(const IndexRange *item, m_ranges)
849 {
850
3/6
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 50 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 50 times.
✗ Branch 8 not taken.
100 text += QString("[%1-%2]").arg(item->m_start + 1).arg(item->m_stop + 1);
851 }
852
853 44 return text;
854 }
855
856 /*!
857 \brief Returns the size of the container of IndexRange instances.
858 */
859 qsizetype
860 1897 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.
1897 return m_ranges.size();
863 }
864
865 /*!
866 \brief Clears all the members of this IndexRangeCollection instance.
867 */
868 void
869 4 IndexRangeCollection::clear()
870 {
871 4 m_comment = "";
872 4 m_ranges.clear();
873 4 }
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