• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MikkelSchubert / adapterremoval / #93

13 Apr 2025 05:01PM UTC coverage: 29.109% (+1.0%) from 28.127%
#93

push

travis-ci

web-flow
rework of sample sequence classes (#120)

* split out read_group into its own files
* split unit tests into files by class
* improvements to doc comments
* various minor refactors and lints
* many additional unit tests

145 of 155 new or added lines in 7 files covered. (93.55%)

2940 of 10100 relevant lines covered (29.11%)

3995.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.0
/src/sequence_sets.hpp
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
// SPDX-FileCopyrightText: 2011 Stinus Lindgreen <stinus@binf.ku.dk>
3
// SPDX-FileCopyrightText: 2014 Mikkel Schubert <mikkelsch@gmail.com>
4
#pragma once
5

6
#include "read_group.hpp"   // for read_group
7
#include "sequence.hpp"     // for for dna_sequence
8
#include <cstddef>          // for size_t
9
#include <initializer_list> // for initializer_list
10
#include <string>           // for string
11
#include <string_view>      // for string_view
12
#include <utility>          // for move
13
#include <vector>           // for vector
14

15
namespace adapterremoval {
16

17
class line_reader_base;
18

19
using string_view_pair = std::pair<std::string_view, std::string_view>;
20

21
/**
22
 * Class for loading/handling adapter adapter sequences.
23
 *
24
 * Adapter sequences are found in one of two orientations:
25
 *  - Read orientation, corresponding to the sequence in input fastq reads
26
 *  - Alignment orientation, corresponding to the orientation used during
27
 *    sequence alignment. For the mate 1 adapter, this is read orientation,
28
 *    but for the mate 2 adapter this is the reverse complement.
29
 */
30
class adapter_set
308✔
31
{
32
public:
33
  /** Initialize empty adapter list. */
34
  adapter_set() = default;
294✔
35

36
  /** Initializes with adapters in read orientation */
37
  adapter_set(std::initializer_list<string_view_pair> args);
38

39
  /** Adds a pair of adapters to the set in read orientation */
40
  void add(dna_sequence adapter1, const dna_sequence& adapter2);
41

42
  /** Generate new adapter set with these barcodes (in read orientation) */
43
  [[nodiscard]] adapter_set add_barcodes(const dna_sequence& barcode1,
44
                                         const dna_sequence& barcode2) const;
45

46
  /**
47
   * Loads adapters in read orientation, clearing existing adapters. Two adapter
48
   * sequences are expected if 'paired_end_mode' is set.
49
   */
50
  void load(const std::string& filename, bool paired_end_mode);
51
  /**
52
   * Loads adapters in read orientation, clearing existing adapters. Two adapter
53
   * sequences are expected if 'paired_end_mode' is set.
54
   */
55
  void load(line_reader_base& reader, bool paired_end_mode);
56

57
  /** Returns the number of adapters/adapter pairs added/loaded */
58
  [[nodiscard]] size_t size() const { return m_adapters.size(); }
2✔
59

60
  /** Returns true if the adapter set is empty */
61
  [[nodiscard]] bool empty() const { return m_adapters.empty(); }
2✔
62

63
  /** Iterator over adapter sequences in alignment orientation */
64
  [[nodiscard]] auto begin() const { return m_adapters.begin(); }
260✔
65

66
  /** Terminal iterator over adapter sequences in alignment orientation */
67
  [[nodiscard]] auto end() const { return m_adapters.end(); }
260✔
68

69
  /** Returns the nth adapter sequences */
70
  [[nodiscard]] const auto& at(size_t n) const { return m_adapters.at(n); }
2✔
71

72
  /** Returns the adapters in read orientation */
73
  [[nodiscard]] sequence_pair_vec to_read_orientation() const;
74

75
  /** Returns true if the adapters (including ordering) are identical  */
76
  [[nodiscard]] bool operator==(const adapter_set& other) const;
77

78
  /** Creates debug representation of an adapter set */
79
  friend std::ostream& operator<<(std::ostream& os, const adapter_set& value);
80

81
private:
82
  //! Adapter sequences in alignment orientation
83
  sequence_pair_vec m_adapters{};
84
};
85

86
/** Represents sequences used for identifying/processing a sample */
87
struct sample_sequences
88
{
89
  sample_sequences() = default;
15✔
90

91
  sample_sequences(dna_sequence barcode_1_, dna_sequence barcode_2_);
92

93
  //! Whether read groups are specified for this set of sequences
94
  bool has_read_group{};
95
  //! Optional read-group for this sample/barcode combination
96
  read_group read_group_{};
97
  //! Barcode expected to be found in mate 1 reads, if any (read orientation)
98
  dna_sequence barcode_1{};
99
  //! Barcode expected to be found in mate 2 reads, if any (read orientation)
100
  dna_sequence barcode_2{};
101
  //! Adapter set with the above barcodes added
102
  adapter_set adapters{};
103

104
  /** Returns true if all members are identical */
105
  [[nodiscard]] bool operator==(const sample_sequences& other) const;
106

107
  /** Creates debug representation of sample sequences */
108
  friend std::ostream& operator<<(std::ostream& os,
109
                                  const sample_sequences& value);
110
};
111

112
/** Represents a (demultiplexing) sample with one or more barcodes */
113
class sample
282✔
114
{
115
public:
116
  /** Creates basic unnamed sample without barcodes */
117
  sample();
118

119
  /** Creates named sample with the specified barcodes */
120
  sample(std::string name, dna_sequence barcode1, dna_sequence barcode2);
121

122
  /** Adds a pair of barcodes in read orientation */
123
  void add(dna_sequence barcode1, dna_sequence barcode2);
124

125
  /** Assigns adapter sequences for each pair of barcodes */
126
  void set_adapters(const adapter_set& adapters);
127

128
  /** Assigns read groups for each pair of barcodes */
129
  void set_read_group(const read_group& read_group_);
130

131
  /** Returns the unique name of this sample */
132
  [[nodiscard]] const auto& name() const { return m_name; }
194✔
133

134
  /** Returns the number of barcode sequences loaded */
135
  [[nodiscard]] size_t size() const { return m_barcodes.size(); }
3✔
136

137
  /** Iterator over adapter sequences in alignment orientation */
138
  [[nodiscard]] auto begin() const { return m_barcodes.begin(); }
306✔
139

140
  /** Terminal iterator over adapter sequences in alignment orientation */
141
  [[nodiscard]] auto end() const { return m_barcodes.end(); }
306✔
142

143
  /** Returns the nth barcode / pair of barcodes */
144
  [[nodiscard]] const auto& at(size_t n) const { return m_barcodes.at(n); }
7✔
145

146
  /** Returns true if name and barcodes are identical */
147
  [[nodiscard]] bool operator==(const sample& other) const;
148

149
  /** Creates debug representation of a sample */
150
  friend std::ostream& operator<<(std::ostream& os, const sample& value);
151

152
private:
153
  //! Unique name associated with this sample
154
  std::string m_name{};
155
  //! Barcodes identifying this sample
156
  std::vector<sample_sequences> m_barcodes{};
157
};
158

159
/** Configuration for loading of barcode tables */
160
class barcode_config
161
{
162
public:
163
  constexpr barcode_config() = default;
42✔
164

165
  /**
166
   * If PE mode is enabled, barcode 1 and 2 together must be unique, otherwise
167
   * barcode 1 sequences must be unique to allow samples to be identified.
168
   */
169
  constexpr auto& paired_end_mode(bool value = true) noexcept
12✔
170
  {
171
    m_paired_end_mode = value;
12✔
172
    return *this;
12✔
173
  }
174

175
  /** Specifies if barcodes are expected in one or both orientations */
NEW
176
  constexpr auto& unidirectional_barcodes(bool value = true) noexcept
×
177
  {
178
    m_unidirectional_barcodes = value;
×
179
    return *this;
×
180
  }
181

182
  /** Enable or disable support for multiple barcodes for the same sample */
NEW
183
  constexpr auto& allow_multiple_barcodes(bool value = true) noexcept
×
184
  {
185
    m_allow_multiple_barcodes = value;
×
186
    return *this;
×
187
  }
188

189
private:
190
  friend class sample_set;
191

192
  //! Whether running in paired or single end mode; is used to determine whether
193
  //! or not samples can be uniquely identified from the barcodes provided
194
  bool m_paired_end_mode = false;
195
  //! Indicates if multiple barcodes/barcode pairs are allowed per sample
196
  bool m_allow_multiple_barcodes = false;
197
  //! Indicates if barcode pairs can be annealed in both orientations
198
  bool m_unidirectional_barcodes = true;
199
};
200

201
/**
202
 * Class for handling user-specified samples for demultiplexing, in addition to
203
 * an 'unidentified' sample representing reads that could not be assigned to a
204
 * sample. In non-demultiplexing mode, the set contains a single, unnamed sample
205
 * with an optional read-group, and no barcode sequences.
206
 */
207
class sample_set
208
{
209
public:
210
  /** Creates sample set with single unnamed sample with empty barcodes */
211
  sample_set();
212
  /** Creates sample set from  lines representing a barcode table */
213
  sample_set(std::initializer_list<std::string_view> lines,
214
             barcode_config config = {});
215

216
  /**
217
   * Sets adapter sequences for all samples, generating unique sequences for
218
   * each set of adapters for each set of barcodes
219
   */
220
  void set_adapters(adapter_set adapters);
221

222
  /** Parses read group string and updates existing samples */
223
  void set_read_group(std::string_view value);
224

225
  /**
226
   * Overrides table with samples specified in a whitespace separated table,
227
   * containing a name column, and one or two barcode columns. Samples are
228
   * updated with the current read group and adapters set.
229
   */
230
  void load(const std::string& filename, const barcode_config& config);
231
  /** Clears existing samples and loads barcodes from a TSV file */
232
  void load(line_reader_base& reader,
233
            const barcode_config& config,
234
            const std::string& filename);
235

236
  /** Convenience function to get sequences for sample / barcode pair */
237
  [[nodiscard]] const auto& get_sequences(const size_t sample,
238
                                          const size_t barcodes) const
239
  {
240
    return m_samples.at(sample).at(barcodes);
241
  }
242

243
  /** Returns the number of (demultiplexing) samples */
244
  [[nodiscard]] size_t size() const { return m_samples.size(); }
31✔
245

246
  /** Iterator over (demultiplexing) samples */
247
  [[nodiscard]] auto begin() const { return m_samples.begin(); }
62✔
248

249
  /** Terminal iterator over (demultiplexing) samples */
250
  [[nodiscard]] auto end() const { return m_samples.end(); }
62✔
251

252
  /** Returns the nth (demultiplexing) sample */
253
  [[nodiscard]] const auto& at(size_t n) const { return m_samples.at(n); }
×
254

255
  /** Returns the original, user-supplied adapter sequences */
256
  [[nodiscard]] const adapter_set& adapters() const { return m_adapters; }
×
257

258
  /** Returns special sample representing uidentified reads */
259
  [[nodiscard]] const auto& unidentified() const { return m_unidentified; }
×
260

261
private:
262
  /** Sets read-group for unidentified reads */
263
  void set_unidentified_read_group(read_group tmpl);
264

265
  /** Adds the reverse complement of barcodes for all samples, if missing */
266
  void add_reversed_barcodes(const barcode_config& config);
267

268
  //! Demultiplexing samples. Names and barcode pairs are both unique
269
  std::vector<sample> m_samples{};
270
  //! Special sample representing unidentified samples;
271
  sample m_unidentified{};
272
  //! User-supplied read group used to generate per-sample read-groups
273
  read_group m_read_group{};
274
  //! User-supplied adapter sequences used to generate per-barcode adapters
275
  adapter_set m_adapters{};
276
};
277

278
} // namespace adapterremoval
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc