44 #include <string_view> 47 #include <range/v3/algorithm/copy.hpp> 48 #include <range/v3/view/remove_if.hpp> 166 template <
typename stream_type,
169 typename offset_type,
170 typename ref_seq_type,
171 typename ref_id_type,
172 typename ref_offset_type,
178 typename tag_dict_type,
179 typename e_value_type,
180 typename bit_score_type>
183 std::unique_ptr<alignment_file_header> & header_ptr,
187 offset_type && offset,
188 ref_seq_type && SEQAN3_DOXYGEN_ONLY(ref_seq),
189 ref_id_type && ref_id,
190 ref_offset_type && ref_offset,
195 tag_dict_type && tag_dict,
196 e_value_type && SEQAN3_DOXYGEN_ONLY(e_value),
197 bit_score_type && SEQAN3_DOXYGEN_ONLY(bit_score))
217 "The seq object must be a std::ranges::ForwardRange over " 218 "letters that model seqan3::alphabet_concept.");
222 "The id object must be a std::ranges::ForwardRange over " 223 "letters that model seqan3::alphabet_concept.");
226 "The offset object must be a std::UnsignedIntegral.");
230 "The ref_seq object must be a std::ranges::ForwardRange " 231 "over letters that model seqan3::alphabet_concept.");
235 "The ref_id object must be a std::ranges::ForwardRange " 236 "over letters that model seqan3::alphabet_concept.");
239 "The ref_offset object must be an std::Integral >= 0.");
241 if (((ref_offset + 1) < 0))
242 throw format_error(
"The ref_offset object must be an std::Integral >= 0.");
245 "The align object must be a std::pair of two ranges whose " 246 "value_type is comparable to seqan3::gap");
251 "The align object must be a std::pair of two ranges whose " 252 "value_type is comparable to seqan3::gap");
255 "The flag object must be a std::UnsignedIntegral.");
258 "The mapq object must be a std::UnsignedIntegral.");
262 "The qual object must be a std::ranges::ForwardRange " 263 "over letters that model seqan3::alphabet_concept.");
266 "The mate object must be a std::tuple of size 3 with " 267 "1) a std::ranges::ForwardRange with a value_type modelling seqan3::alphabet_concept, " 268 "2) an std::UnsignedIntegral, and" 269 "3) an std::UnsignedIntegral.");
275 "The mate object must be a std::tuple of size 3 with " 276 "1) a std::ranges::ForwardRange with a value_type modelling seqan3::alphabet_concept, " 277 "2) an std::UnsignedIntegral, and" 278 "3) an std::UnsignedIntegral.");
281 "The tag_dict object must be of type seqan3::sam_tag_dictionary.");
287 throw format_error(
"If you specify an align object you must also specify the seq object. " 288 "Hint: Check if offset needs to be set to if soft-clipping is present.");
292 if ((header_ptr->ref_dict).count(std::string(ref_id)) == 0)
293 throw format_error(std::string(
"The ref_id '") + std::string(ref_id) +
294 "' was not in the list of references");
302 write_header(stream, options, header_ptr);
303 written_header =
true;
310 char const separator{
'\t'};
312 write_range(stream_it, std::forward<id_type>(
id));
316 stream << std::forward<flag_type>(flag) << separator;
318 write_range(stream_it, std::forward<ref_id_type>(ref_id));
322 stream << (ref_offset + 1) << separator;
324 stream << std::forward<mapq_type>(mapq) << separator;
326 if (!
empty(get<1>(align)))
332 size_t off_end{seq.size() - offset};
333 for (
auto chr : get<1>(align))
336 off_end -= (get<1>(align)).size();
338 write_range(stream_it,
339 detail::get_cigar_string(std::forward<align_type>(align),
340 std::forward<offset_type>(offset),
350 write_range(stream_it, get<0>(std::forward<mate_type>(mate)));
354 stream << get<1>(std::forward<mate_type>(mate)) << separator;
356 stream << get<2>(std::forward<mate_type>(mate)) << separator;
358 write_range(stream_it, std::forward<seq_type>(seq));
362 write_range(stream_it, std::forward<qual_type>(qual));
364 write_tag_fields(stream, std::forward<tag_dict_type>(tag_dict), separator);
372 static constexpr
char format_version[4] =
"1.6";
375 bool written_header{
false};
384 template <
typename stream_it_t,
typename field_type>
388 void write_range(stream_it_t & stream_it, field_type && field_value)
390 if (
empty(field_value))
403 template <
typename stream_t>
404 void write_tag_fields(stream_t & stream,
sam_tag_dictionary const & tag_dict,
char const separator)
406 auto stream_variant_fn = [&stream] (
auto && arg)
416 if (arg.begin() != arg.end())
418 for (
auto it = arg.begin(); it != (arg.end() - 1); ++it)
419 stream << *it <<
",";
421 stream << *(arg.end() - 1);
426 for (
auto & [tag, variant] : tag_dict)
430 char char0 = tag / 256;
431 char char1 = tag % 256;
433 stream << char0 << char1 <<
':' << detail::sam_tag_type_char[variant.index()] <<
':';
435 if (detail::sam_tag_type_char_extra[variant.index()] !=
'\0')
436 stream << detail::sam_tag_type_char_extra[variant.index()] <<
',';
438 std::visit(stream_variant_fn, variant);
458 template <
typename stream_t>
459 void write_header(stream_t & stream,
461 std::unique_ptr<alignment_file_header> & header_ptr)
463 if (header_ptr !=
nullptr)
471 if (!(header_ptr->sorting ==
"unknown" ||
472 header_ptr->sorting ==
"unsorted" ||
473 header_ptr->sorting ==
"queryname" ||
474 header_ptr->sorting ==
"coordinate" ))
475 throw format_error{
"SAM format error: The header_ptr->sorting member must be " 476 "one of [unknown, unsorted, queryname, coordinate]."};
478 if (!(header_ptr->grouping ==
"none" ||
479 header_ptr->grouping ==
"query" ||
480 header_ptr->grouping ==
"reference"))
481 throw format_error{
"SAM format error: The header_ptr->grouping member must be " 482 "one of [none, query, reference]."};
501 stream <<
"@HD\tVN:";
502 stream << format_version;
504 if (!header_ptr->sorting.empty())
505 stream <<
"\tSO:" << header_ptr->sorting;
507 if (!header_ptr->grouping.empty())
508 stream <<
"\tGO:" << header_ptr->grouping;
513 for (
auto const & [ref_name, ref_info] : header_ptr->ref_dict)
516 <<
"\tSN:" << ref_name
517 <<
"\tLN:" << get<0>(ref_info);
519 if (!get<1>(ref_info).empty())
520 stream <<
"\t" << get<1>(ref_info);
526 for (
auto const & read_group : header_ptr->read_groups)
529 <<
"\tID:" << get<0>(read_group);
531 if (!get<1>(read_group).empty())
532 stream <<
"\t" << get<1>(read_group);
538 for (
auto const & program : header_ptr->program_infos)
541 <<
"\tID:" << program.id;
543 if (!program.name.empty())
544 stream <<
"\tPN:" << program.name;
546 if (!program.command_line_call.empty())
547 stream <<
"\tCL:" << program.command_line_call;
549 if (!program.previous.empty())
550 stream <<
"\tPP:" << program.previous;
552 if (!program.description.empty())
553 stream <<
"\tDS:" << program.description;
555 if (!program.version.empty())
556 stream <<
"\tVN:" << program.version;
562 for (
auto const & comment : header_ptr->comments)
564 stream <<
"@CO\t" << comment;
Provides seqan3::view::get.
The (most general) container concept as defined by the standard library.
auto constexpr take_until
A view adaptor that returns elements from the underlying range until the functor evaluates to true (o...
Definition: take_until.hpp:448
The alphabet of a gap character '-'.
Definition: gap.hpp:62
Provides the seqan3::sam_tag_dictionary class and auxiliaries.
Provides C++20 additions to the <iterator> header.
Provides seqan3::detail::ignore_output_iterator for writing to null stream.
Whether a type behaves like a tuple.
::ranges::copy copy
Alias for ranges::copy. Copies a range of elements to a new location.
Definition: ranges:200
bool add_carriage_return
The default plain text line-ending is "\n", but on Windows an additional carriage return is recommend...
Definition: output_options.hpp:54
The generic alphabet concept that covers most data types used in ranges.This is the core alphabet con...
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:58
Auxiliary functions for the alignment IO.
::ranges::ostreambuf_iterator ostreambuf_iterator
Alias for ranges::ostreambuf_iterator. Output iterator that writes to std::basic_streambuf.
Definition: ranges:230
Provides seqan3::alignment_file_output_options.
The concept Integral is satisfied if and only if T is an integral type.
Provides seqan3::view::take_until and seqan3::view::take_until_or_throw.
Provides seqan3::tuple_like_concept.
Provides various utility functions.
Provides seqan3::view::char_to.
Adaptations of concepts from the Ranges TS.
bool sam_require_header
Whether to require a header for SAM files.
Definition: output_options.hpp:68
The concept std::Same<T, U> is satisfied if and only if T and U denote the same type.
auto const to_char
A view that calls seqan3::to_char() on each element in the input range.
Definition: to_char.hpp:88
Specifies requirements of a Range type for which begin returns a type that models std::ForwardIterato...
The options type defines various option members that influence the behavior of all or some formats...
Definition: output_options.hpp:49
auto constexpr is_space
Checks whether c is a space character.
Definition: parse_condition.hpp:171
::ranges::empty empty
Alias for ranges::empty. Checks whether a range is empty.
Definition: ranges:205
Provides seqan3::view::to_char.
Provides parse conditions for tokenization.
Requires std::detail::WeaklyEqualityComparableWitht<t1,t2>, but also that t1 and t2, as well as their common_reference_t satisfy std::EqualityComparable.
Provides various metafunctions used by the range module.
The concept std::UnsignedIntegral is satisfied if and only if T is an integral type and std::is_signe...
The SAM tag dictionary class that stores all optional SAM fields.
Definition: sam_tag_dictionary.hpp:339