diff --git a/doc/main_page.md b/doc/main_page.md index 29fe1aa..6909d4d 100644 --- a/doc/main_page.md +++ b/doc/main_page.md @@ -1,12 +1,15 @@ # Welcome {#mainpage} Welcome to the documentation of the B.I.O. library. -This web-site contains the API reference (documentation of our interfaces) and more elaborate Tutorials and -How-Tos. +This web-site contains the API reference (documentation of our interfaces) and some small Tutorials and HowTos. + +B.I.O makes use of SeqAn3 and it is recommended to have a look at [their documentation](https://docs.seqan.de) first. ## Overview +This section contains a very short overview of the most important parts of the library. + ### General IO Utilities @@ -20,7 +23,7 @@ The transparent streams can be used in place of the standard library streams. Th compressions such as GZip, BZip2 and BGZip. -### Readers and Writers +### Record-based I/O | Reader | Writer | Description | diff --git a/doc/record_based/1_introduction.md b/doc/record_based/1_introduction.md new file mode 100644 index 0000000..a9fe572 --- /dev/null +++ b/doc/record_based/1_introduction.md @@ -0,0 +1,50 @@ +# Introduction {#record_based_intro} + +Most files in bioinformatics are comprised of *records*, i.e. multiple, indepedent entries that each consist of one or +more *fields*. +For example, a FastA file contains one or more sequence records that each contain an ID field and sequence field. + +[TOC] + +``` +>myseq1 +ACGT + +>myseq2 +GAGGA + +>myseq3 +ACTA +``` + +
+↓↓↓↓↓↓↓ +
+ + +| ID field | sequence field | +|:----------:|:--------------:| +| "myseq1" | "ACGT" | +| "myseq2" | "GAGGA" | +| "myseq3" | "ACTA" | + +Each line in this table is conceptionally "a record", and each file is modeled as a series of these records. +The process of "reading a file", is transforming the on-disk representation displayed above into the "abstraction" shown below. +The process of "writing a file" is the reverse. + +Details on how records are defined is available here: \ref record_faq + +## Readers + +So called *readers* are responsible for detecting the format and decoding a file into a series of records: + +\snippet test/snippet/seq_io/seq_io_reader.cpp simple_usage_file + +The reader is an *input range* which is C++ terminology for "something that you can iterate over (once)". +The last bit is important, it implies that once you reach the end, the reader will be "empty". To iterate over it again, you need to recreate it. + + + +## Writers + +TODO diff --git a/doc/record_based/2_record_faq.md b/doc/record_based/2_record_faq.md new file mode 100644 index 0000000..860f395 --- /dev/null +++ b/doc/record_based/2_record_faq.md @@ -0,0 +1,156 @@ +# Record FAQ {#record_faq} + +Records in B.I.O. are of implemented as a specialisation of the bio::record template.¹ +This behaves very similar to a std::tuple with the difference that a bio::field identifier is associated with every +element and a corresponding member function is provided, so you can easily access the elements without knowing the order. + +¹ With the exception of bio::plain_io which uses bio::plain_io::record. + +[TOC] + +\note This page contains details on how records are defined. It is meant to provide a better understanding of the design and performance implications. We recommend starting with the snippets shown in the API (e.g. bio::seq_io::reader, bio::var_io::reader, …) and only return to this page if you have questions or want to fine-tune things. + +## What is the full type of my record? {#record_type} + +Most records you interact with are produced by readers. + +\snippet test/snippet/seq_io/seq_io_reader.cpp simple_usage_file + +In this example, `rec` is the record and with each iteration of the loop, a new record is generated from the file. The exact type of the record depends on the reader. In the above example, it is: + +\snippet test/snippet/seq_io/seq_io_reader.cpp simple_usage_file_type + +That is quite long and difficulat to remember (even though definitions of X* and Y* are omitted here), +so we write `auto &` instead. +But it is important to know which fields are contained in the record (in this case ID, SEQ and QUAL). +The documentation for the reader will tell you this, e.g. bio::seq_io::reader. + +## How can I access the fields? + +The easiest way to access a field, is by calling the respective member function: + +\snippet test/snippet/seq_io/seq_io_reader.cpp simple_usage_file + +Here, `.id()` (bio::record#id()) and `.seq()` (bio::record#seq()) are used to access the fields. Note, that the +documentation has entries for all field-accessor member functions, but it depends on the specific specialisation +(used by the reader) whether that function is available. +So, on the record defined by bio::seq_io::reader above, the members `.id()`, `.seq()`, `.qual()` are available, but +the member `.pos()` would not be. + +When the number of fields in the record is low and you know the order, you can also use +[structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) +to decompose the record into its fields: + +\snippet test/snippet/seq_io/seq_io_reader.cpp decomposed + +Note that the order of the fields is fixed (in this case it is defined by bio::seq_io::default_field_ids). +It is independent of the names you give to the bindings, so this syntax is error-prone when used with large records +(e.g. those defined by bio::var_io::reader). + +In generic contexts, you can also access fields via `get<0>(rec)` (returns the 0-th field in the record) or +`get(rec)` (the same as calling `rec.id()`); but most users will never need this. + + +## Does my record own the data? (Shallow vs deep records) {#shallow_vs_deep} + +As shown above, every field has an identifier (e.g. bio::field::id) and a type (e.g. std::string_view). +You may have wondered, why std::string_view is used as a type and what these `transform_view`s are. +These imply that the record is a *shallow* data structure, i.e. the fields *appear* like strings or vectors, but they +are implemented more like references or pointers. +See the SeqAn3 documentation for an in-depth [Tutorial on Ranges and Views](http://docs.seqan.de/seqan/3-master-user/tutorial_ranges.html). + +Shallow records imply fewer memory allocations and/or copy operations during reading. This results in a **better +performance** but also in some important limitations: + +* Shallow records cannot be modified (as easily²). +* Shallow records cannot be "stored"; they depend on internal caches and buffers of the reader and become invalid +as soon as the next record is read from the file. + + +If you need to change a record in-place and/or "store" the record for longer than one iteration of the reader, you need to use *deep records* instead. +You can tell the reader that you want deep records by providing the respective options: + +\snippet test/snippet/seq_io/seq_io_reader.cpp options2 + +This snippet behaves similar to the previous one, except that the type of `rec` is now the following: + +\snippet test/snippet/seq_io/seq_io_reader.cpp options2_type + +This allows you to call std::vector's `.push_back()` member function (which is not possible in the default case). +Creating this kind of record is likely a bit slower than the shallow record. + +**Summary** + +* The records generated by readers are *shallow* by default. +* This setting has the best performance; but it is less flexible than a *deep* record. +* Readers can be configured to produce *deep* records via the options. + +² Some modifying operations are possible on views, too, but this depends on the exact types. + +## How can I change the field types? + +In the previous section, we showed how to change the field types from being shallow to deep. +For some readers, more options are available, e.g. bio::seq_io::reader assumes nucleotide data for the SEQ field by default, but you might want to read protein data instead. + +\snippet test/snippet/seq_io/seq_io_reader.cpp options + +The snippet above illustrates how the alphabet can be changed (and how to provide another option at the same time). + +Instead of using these pre-defined `field_types`, you can also define them completely manually. You can decide to even read only a subset of the fields by changing the `.field_ids` member: + +\snippet test/snippet/seq_io/seq_io_reader_options.cpp example_advanced2 + +This code makes FASTA the only legal format and creates records with only the sequence field asa std::string. + +But you can also use this mechanism to make some fields shallow and other fields deep. It also allows +to choose different container types. +See the API documentation of the respective `reader_options` for more advanced use-cases and the +exact restrictions on allowed types. + +## How can I create record variables? + +There are various easy ways to create a bio::record that do not involve manually providing the template arguments: + +1. Deduce from the reader. +2. Use an alias. +3. Use bio::make_record or bio::tie_record. + +### Deduce from the reader {#record_type_from_reader} + +When iterating over a reader, it is easy to use `auto &` to deduce the record type, but sometimes you need +the record type outside of the for-loop or in a separate context. + +This snippet demonstrates how to read an interleaved FastQ file and process the read pairs together (at every second iteration of the loop): + +\snippet test/snippet/detail/reader_base.cpp read_pair_processing + +To to this, you need to use deep records, because shallow records become invalid after the loop iteration. +Note how it is possible to "ask" the reader for the type of its record to create the local variable. + +### Record type aliases {#record_aliases} + +When writing a file without reading a file previously, you can use one of the predefined aliases: + +* bio::var_io::default_record + +This longer example illustrates using an alias: + +\snippet test/snippet/var_io/var_io_writer.cpp creation +\snippet test/snippet/var_io/var_io_writer.cpp simple_usage_file + +Here bio::var_io::default_record is the type that a bio::var_io::reader would generate if it is defined without any options, **except that the alias is deep by default.** +This is based on the assumption that aliases are typically used to define local variables whose values you want to change. + +### Making and tying records {#record_make_tie} + +There are convenience functions for making and tying records, similar to std::make_tuple and std::tie: +\snippet test/snippet/record.cpp make_and_tie_record + +The type of rec1 is: +\snippet test/snippet/record.cpp make_and_tie_record_type_rec1 + +The type of rec2 is: +\snippet test/snippet/record.cpp make_and_tie_record_type_rec2 + +When creating a record from existing variables, you can use bio::tie_record to avoid needless copies. +Instead of manually entering the identifiers as a bio::vtag, you can use bio::seq_io::default_field_ids (or the respective defaults of another reader/writer). diff --git a/include/bio/detail/reader_base.hpp b/include/bio/detail/reader_base.hpp index 489ef7a..cbd498c 100644 --- a/include/bio/detail/reader_base.hpp +++ b/include/bio/detail/reader_base.hpp @@ -37,6 +37,7 @@ namespace bio // ---------------------------------------------------------------------------- /*!\brief This is a (non-CRTP) base-class for I/O readers. + * \ingroup bio * \tparam options_t Type of the reader options. * \details * @@ -72,7 +73,19 @@ class reader_base : public std::ranges::view_base * \brief The exact type of the record depends on the options! * \{ */ - //!\brief The type of the record, a specialisation of bio::record; acts as a tuple of the selected field types. + /*!\brief The type of the record, a specialisation of bio::record. + * \details + * + * ### Example + * + * This snippet demonstrates how to read an interleaved FastQ file and process the read pairs + * together (at every second iteration of the loop): + * + * \snippet test/snippet/detail/reader_base.cpp read_pair_processing + * + * To be able to easily backup the first record of a mate-pair, you need to create a temporary + * variable (`last_record`). This type alias helps define it. + */ using record_type = record; //!\brief The iterator type of this view (an input iterator). using iterator = detail::in_file_iterator; diff --git a/include/bio/misc.hpp b/include/bio/misc.hpp index fd7140b..bcc98f1 100644 --- a/include/bio/misc.hpp +++ b/include/bio/misc.hpp @@ -34,6 +34,8 @@ namespace bio * Typically used to configure a class template to have members that are vectors/strings VS members that are views. * The "shallow" version of such a class is typically cheap to copy (no dynamic memory) while the "deep" version * is expensive to copy (holds dynamic memory). + * + * See \ref shallow_vs_deep on what this means in practice. */ enum class ownership { diff --git a/include/bio/record.hpp b/include/bio/record.hpp index e628d37..464563c 100644 --- a/include/bio/record.hpp +++ b/include/bio/record.hpp @@ -107,23 +107,22 @@ namespace bio /*!\brief The class template that file records are based on; behaves like an std::tuple. * \implements seqan3::tuple_like * \ingroup bio - * \tparam field_types The types of the fields in this record as a seqan3::type_list. * \tparam field_ids A vtag_t type with bio::field IDs corresponding to field_types. + * \tparam field_types The types of the fields in this record as a seqan3::type_list. * \details * - * This class template behaves just like an std::tuple, with the exception that it provides an additional + * This class template behaves like a std::tuple, with the exception that it provides an additional * get-interface that takes a bio::field identifier. The traditional get interfaces (via index and * via type) are also supported, but discouraged, because accessing via bio::field is unambiguous and * better readable. * - * ### Example + * In addition to the get()-interfaces, member accessors are provided with the same name as the fields. * - * For input files this template is specialised automatically and provided by the file via its `record_type` member. - * For output files you my define it locally and pass instances of this to the output file's `push_back()`. + * See bio::seq_io::reader for how this data structure is used in practice. * - * This is how it works: + * See #make_record() and #tie_record() for easy ways to create stand-alone record variables. * - * \todo include test/snippet/io/record_2.cpp + * See the \ref record_faq for more details. */ template struct record : seqan3::detail::transfer_template_args_onto_t @@ -372,15 +371,22 @@ auto const && get(record const && r) // make_record //------------------------------------------------------------------------------- -/*!\brief Create a bio::record and deduce type from arguments (like std::make_tuple for std::tuple). +/*!\brief Create a deep bio::record from the arguments (like std::make_tuple for std::tuple). + * \param[in] tag A tag that specifies the identifiers of the subsequent arguments. + * \param[in] fields The arguments to put into the record. + * \returns A bio::record with copies of the field arguments. * \details * + * The record will contain copies of the arguments. + * + * For more information, see \ref record_type and \ref record_make_tie + * * ### Example * - * TODO + * \snippet test/snippet/record.cpp make_and_tie_record */ template -constexpr auto make_record(vtag_t, field_type_ts &&... fields) +constexpr auto make_record(vtag_t BIO_DOXYGEN_ONLY(tag), field_type_ts &&... fields) -> record, seqan3::type_list...>> { return {std::forward(fields)...}; @@ -390,15 +396,22 @@ constexpr auto make_record(vtag_t, field_type_ts &&... fields) // tie_record //------------------------------------------------------------------------------- -/*!\brief Create a bio::record of references (like std::tie for std::tuple). +/*!\brief Create a shallow bio::record from the arguments (like std::tie for std::tuple). + * \param[in] tag A tag that specifies the identifiers of the subsequent arguments. + * \param[in] fields The arguments to represent in the record. + * \returns A bio::record with references to the field arguments. * \details * + * The record will contain references to the arguments. + * + * For more information, see \ref record_type and \ref record_make_tie + * * ### Example * - * TODO + * \snippet test/snippet/record.cpp make_and_tie_record */ template -constexpr auto tie_record(vtag_t, field_type_ts &... fields) +constexpr auto tie_record(vtag_t BIO_DOXYGEN_ONLY(tag), field_type_ts &... fields) -> record, seqan3::type_list> { return {fields...}; diff --git a/include/bio/seq_io/reader.hpp b/include/bio/seq_io/reader.hpp index 79a6465..419fa0a 100644 --- a/include/bio/seq_io/reader.hpp +++ b/include/bio/seq_io/reader.hpp @@ -92,6 +92,11 @@ namespace bio::seq_io * at the first whitespace: * \snippet test/snippet/seq_io/seq_io_reader.cpp options * + * If you need to modify or store the records, request *deep records* from the reader: + * \snippet test/snippet/seq_io/seq_io_reader.cpp options2 + * + * For more information on *shallow* vs *deep*, see \ref shallow_vs_deep + * * For more advanced options, see bio::seq_io::reader_options. */ template diff --git a/include/bio/seq_io/reader_options.hpp b/include/bio/seq_io/reader_options.hpp index c706e59..5acae27 100644 --- a/include/bio/seq_io/reader_options.hpp +++ b/include/bio/seq_io/reader_options.hpp @@ -104,6 +104,8 @@ inline constinit auto field_types_protein = field_types; //!\} diff --git a/include/bio/var_io/reader.hpp b/include/bio/var_io/reader.hpp index d7d6abc..50eacaa 100644 --- a/include/bio/var_io/reader.hpp +++ b/include/bio/var_io/reader.hpp @@ -55,8 +55,7 @@ namespace bio::var_io * are returned by default also correspond to VCF specification (i.e. 1-based positions, string as strings and not * as numbers) **with one exception:** the genotypes are not grouped by sample (as in the VCF format) but by * genotype field (as in the BCF format). - * This results in a notably better performance when reading BCF files. See below for information on how to change - * this. + * This results in a notably better performance when reading BCF files. * * This reader supports the following formats: * diff --git a/include/bio/var_io/reader_options.hpp b/include/bio/var_io/reader_options.hpp index 9f4c547..90e7ef3 100644 --- a/include/bio/var_io/reader_options.hpp +++ b/include/bio/var_io/reader_options.hpp @@ -24,7 +24,6 @@ #include #include #include - #include #include @@ -90,7 +89,7 @@ namespace bio::var_io * and all other tools that deal with VCF/BCF. * * Beyond that, a wide variety of types are supported per field (see below), but most users will be happy - * with one of the predefined sets. + * with one of the predefined tags. * * ### Pre-defined tags * @@ -103,29 +102,8 @@ namespace bio::var_io * * When reading and writing, you need to make sure that the IDX values in the output header are the same as in the * input header, otherwise your record fields might change meaning or even become invalid. * - * Both styles are "shallow" by default, but can be configured to be "deep": - * - * 1. shallow (bio::ownership::shallow) - * * The record contains light-weight data structures like views. - * * Record cannot be "stored"; it depends on internal caches and buffers, and it becomes invalid - * as soon as the next record is read from the file. - * 2. deep (bio::ownership::deep) - * * The record is self-contained; sequences and strings are stored in containers. - * * Record can be copied or stored and can "live on" independently of the reader. - * - * This example shows how to use deep records: - * - * \snippet test/snippet/var_io/var_io_reader_options.cpp field_types_deep - * - * Performance implications: - * * Shallow records imply fewer allocations and lower overhead during reading. - * * If you know that you need to copy your fields anyway, using a deep record can be faster than using a shallow - * record and copying the data "manually" out of that (because certain internal caches are re-used to create deep - * records). - * * field_types_bcf_style is faster than field_types, but for the shallow variants - * there is almost no difference. - * - * TODO some of this should be moved to a general documentation page on configuring records; shallow vs deep; etc + * Both styles are *shallow* by default, but can be configured to be *deep*. + * For more details, see \ref shallow_vs_deep * * ### Manual configuration * @@ -172,9 +150,7 @@ namespace bio::var_io * \snippet test/snippet/var_io/var_io_reader_options.cpp field_types_expert * * Reading fewer fields than available may provide a noticeable speed-up since only the - * requested fields are actually parsed. Any field may also be set to `std::span` which - * results in no parsing happening for that field. - * + * requested fields are actually parsed. */ template ), diff --git a/test/documentation/DoxygenLayout.xml b/test/documentation/DoxygenLayout.xml index 74d11a2..7941884 100644 --- a/test/documentation/DoxygenLayout.xml +++ b/test/documentation/DoxygenLayout.xml @@ -4,6 +4,10 @@ + + + + diff --git a/test/snippet/detail/reader_base.cpp b/test/snippet/detail/reader_base.cpp new file mode 100644 index 0000000..5bb77db --- /dev/null +++ b/test/snippet/detail/reader_base.cpp @@ -0,0 +1,52 @@ +#include + +#include +#include + +#include "../../unit/seq_io/data.hpp" + +void process_read_pair(auto&&, auto&&) {} + +int main() +{ + //================= PRE ========================== + { + std::ofstream os{"example.fastq", std::ios::binary}; + os << interleaved_fastq; + } + + //================= SNIPPETS ====================== +{ +//![read_pair_processing] +// choose deep records so they can be copied/moved +bio::seq_io::reader_options options{ .field_types = bio::seq_io::field_types }; + +// open an interleaved paired-end FastQ file +bio::seq_io::reader reader{"example.fastq", options}; + +// ask the reader for its record_type; create a variable to hold previous record +decltype(reader)::record_type last_record; + +bool is_first_of_pair = true; +for (auto & current_record : reader) +{ + if (is_first_of_pair) + { + // backup the current record; only possible because it is deep + std::swap(current_record, last_record); + } + else // is second of pair + { + // do something with the pair + process_read_pair(last_record, current_record); + } + + is_first_of_pair = !is_first_of_pair; +} +//![read_pair_processing] + +} + + //================= POST ========================== + std::filesystem::remove("example.fastq"); +} diff --git a/test/snippet/record.cpp b/test/snippet/record.cpp new file mode 100644 index 0000000..576957f --- /dev/null +++ b/test/snippet/record.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include + +int main() +{ +//![make_and_tie_record] +using namespace seqan3::literals; + +std::string id = "seq1"; +std::vector seq = "ACGT"_dna5; +std::vector qual = "!!!!"_phred42; + +/* This creates a *deep* record; it contains a copy of the above strings/vectors */ +auto rec1 = bio::make_record(bio::vtag, // identifiers + id, seq, qual); // values + + +/* This creates a *shallow* record; it contains references to the above strings/vectors */ +auto rec2 = bio::tie_record(bio::vtag, // identifiers + id, seq, qual); // values +//![make_and_tie_record] + +static_assert(std::same_as, + seqan3::type_list, + std::vector>> +//![make_and_tie_record_type_rec1] + >); + +static_assert(std::same_as, + seqan3::type_list &, + std::vector &>> +//![make_and_tie_record_type_rec2] + >); +} diff --git a/test/snippet/seq_io/seq_io_reader.cpp b/test/snippet/seq_io/seq_io_reader.cpp index 2604e73..a4c355e 100644 --- a/test/snippet/seq_io/seq_io_reader.cpp +++ b/test/snippet/seq_io/seq_io_reader.cpp @@ -5,6 +5,10 @@ #include "../../unit/seq_io/data.hpp" + +template +std::pair get_arg_t(std::ranges::transform_view); + int main() { //================= PRE ========================== @@ -27,6 +31,21 @@ for (auto & rec : reader) seqan3::debug_stream << "Seq: " << rec.seq() << '\n'; } //![simple_usage_file] + +using X1 = decltype(get_arg_t(reader.begin()->seq()).first); +using X2 = decltype(get_arg_t(reader.begin()->seq()).second); +using Y1 = decltype(get_arg_t(reader.begin()->qual()).first); +using Y2 = decltype(get_arg_t(reader.begin()->qual()).second); + +static_assert(std::same_as, // identifiers of the fields + seqan3::type_list, // type of the SEQ field + std::ranges::transform_view>> // type of the QUAL field +//![simple_usage_file_type] + >); + } { @@ -45,10 +64,10 @@ for (auto & rec : reader) //![decomposed] bio::seq_io::reader reader{"example.fasta"}; -for (auto & [ id, seq, qual ] : reader) +for (auto & [ i, s, q ] : reader) { - seqan3::debug_stream << "ID: " << id << '\n'; - seqan3::debug_stream << "Seq: " << seq << '\n'; + seqan3::debug_stream << "ID: " << i << '\n'; + seqan3::debug_stream << "Seq: " << s << '\n'; } //![decomposed] } @@ -70,8 +89,8 @@ for (auto & rec : reader | std::views::filter(min_length) | std::views::take(5)) { //![options] bio::seq_io::reader reader{"example.fasta", - bio::seq_io::reader_options{ .field_types = bio::seq_io::field_types_protein, - .truncate_ids = true }}; + bio::seq_io::reader_options{.field_types = bio::seq_io::field_types_protein, + .truncate_ids = true }}; for (auto & rec : reader) { @@ -81,6 +100,33 @@ for (auto & rec : reader) //![options] } +{ +//![options2] +using namespace seqan3::literals; + +bio::seq_io::reader reader{"example.fasta", + bio::seq_io::reader_options{.field_types = bio::seq_io::field_types}}; + +for (auto & rec : reader) +{ + seqan3::debug_stream << "ID: " << rec.id() << '\n'; + seqan3::debug_stream << "Seq: " << rec.seq() << '\n'; + + rec.seq().push_back('A'_dna5); // ← this is not possible with shallow records (default) + seqan3::debug_stream << "SeqM: " << rec.seq() << '\n'; +} +//![options2] + +static_assert(std::same_as, // identifiers of the fields + seqan3::type_list, // type of the SEQ field + std::vector>> // type of the QUAL field +//![options2_type] + >); +} + //================= POST ========================== std::filesystem::remove("example.fasta"); } diff --git a/test/snippet/seq_io/seq_io_reader.err b/test/snippet/seq_io/seq_io_reader.err index 5c05c65..f46377e 100644 --- a/test/snippet/seq_io/seq_io_reader.err +++ b/test/snippet/seq_io/seq_io_reader.err @@ -46,3 +46,18 @@ ID: ID4 Seq: ACGTTTA ID: ID5 Seq: ACGTTTAACGTTTTTTTT +ID: ID1 +Seq: ACGTTTTTTTTTTTTTTT +SeqM: ACGTTTTTTTTTTTTTTTA +ID: ID2 +Seq: ACGTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT +SeqM: ACGTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTA +ID: ID3 lala +Seq: ACGTTTAACGTTTTTTTT +SeqM: ACGTTTAACGTTTTTTTTA +ID: ID4 +Seq: ACGTTTA +SeqM: ACGTTTAA +ID: ID5 lala +Seq: ACGTTTAACGTTTTTTTT +SeqM: ACGTTTAACGTTTTTTTTA diff --git a/test/snippet/seq_io/seq_io_reader_options.cpp b/test/snippet/seq_io/seq_io_reader_options.cpp index bb66c3f..d88ba99 100644 --- a/test/snippet/seq_io/seq_io_reader_options.cpp +++ b/test/snippet/seq_io/seq_io_reader_options.cpp @@ -4,14 +4,6 @@ int main() { -{ -//![example_custom] -bio::seq_io::reader_options options -{ - .field_types = bio::seq_io::field_types, -}; -//![example_custom] -} { //![example_simple] diff --git a/test/unit/seq_io/data.hpp b/test/unit/seq_io/data.hpp index 516badc..037ce0e 100644 --- a/test/unit/seq_io/data.hpp +++ b/test/unit/seq_io/data.hpp @@ -30,3 +30,22 @@ inline constexpr std::string_view input_bgzipped{ "\x39\xa6\xb8\xd5\x02\x00\xcd\x3b\x57\x80\xba\x00\x00\x00\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\xff\x06\x00\x42\x43\x02" "\x00\x1b\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00", 100}; + +inline constexpr std::string_view interleaved_fastq = + R"raw(@M10991:61:000000000-A7EML:1:1101:14011:1001 1:N:0:28 +NGCTCCTAGGTCGGCATGATGGGGGAAGGAGAGCATGGGAAGAAATGAGAGAGTAGCAA ++ +#8BCCGGGGGFEFECFGGGGGGGGG@;FFGGGEG@FF