SeqAn3  3.0.0
The Modern C++ library for sequence analysis.
debug_matrix.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik
4 // This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5 // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6 // -----------------------------------------------------------------------------------------------------
7 
13 #pragma once
14 
15 #include <iomanip>
16 
21 
22 namespace seqan3::detail
23 {
24 
56 template <Matrix matrix_t, typename first_sequence_t = std::nullopt_t, typename second_sequence_t = std::nullopt_t>
57 class debug_matrix
58 {
59 protected:
61  static constexpr bool has_first_sequence = !std::is_same_v<std::decay_t<first_sequence_t>, std::nullopt_t>;
63  static constexpr bool has_second_sequence = !std::is_same_v<std::decay_t<second_sequence_t>, std::nullopt_t>;
65  using entry_t = typename std::remove_reference_t<matrix_t>::entry_type;
67  static constexpr bool is_traceback_matrix = std::is_same_v<std::decay_t<entry_t>, trace_directions>;
70  static constexpr bool is_optional_score = is_type_specialisation_of_v<entry_t, std::optional>;
71 public:
73  using entry_type = std::conditional_t<is_traceback_matrix || is_optional_score,
74  entry_t,
76 
80  debug_matrix() = default;
81  debug_matrix(debug_matrix const &) = default;
82  debug_matrix(debug_matrix &&) = default;
83  debug_matrix & operator=(debug_matrix const &) = default;
84  debug_matrix & operator=(debug_matrix &&) = default;
85  ~debug_matrix() = default;
86 
90  debug_matrix(matrix_t matrix)
91  : debug_matrix(std::forward<matrix_t>(matrix), std::nullopt, std::nullopt)
92  {}
93 
99  debug_matrix(matrix_t matrix, first_sequence_t first_sequence, second_sequence_t second_sequence)
100  : _matrix{std::forward<matrix_t>(matrix)},
101  _first_sequence{std::forward<first_sequence_t>(first_sequence)},
102  _second_sequence{std::forward<second_sequence_t>(second_sequence)}
103  {
104  if constexpr(has_first_sequence)
105  {
106  assert(_matrix.cols() <= _first_sequence.size() + 1u);
107  }
108 
109  if constexpr(has_second_sequence)
110  {
111  assert(_matrix.rows() <= _second_sequence.size() + 1u);
112  }
113  }
115 
117  size_t rows() const noexcept
118  {
119  if (!_transpose)
120  return _rows.value_or(_matrix.rows());
121  else
122  return _cols.value_or(_matrix.cols());
123  }
124 
126  size_t cols() const noexcept
127  {
128  if (!_transpose)
129  return _cols.value_or(_matrix.cols());
130  else
131  return _rows.value_or(_matrix.rows());
132  }
133 
135  first_sequence_t const & first_sequence() const noexcept
136  {
137  if (!_transpose)
138  return _first_sequence;
139  else
140  return _second_sequence;
141  }
142 
144  second_sequence_t const & second_sequence() const noexcept
145  {
146  if (!_transpose)
147  return _second_sequence;
148  else
149  return _first_sequence;
150  }
151 
153  entry_type at(size_t const row, size_t const col) const noexcept
154  {
155  assert(row < rows() && col < cols());
156 
157  size_t const _row = !_transpose ? row : col;
158  size_t const _col = !_transpose ? col : row;
159 
160  if (!_masking_matrix.has_value() || _masking_matrix.value().at(_row, _col))
161  {
162  entry_t const & entry = _matrix.at(_row, _col);
163 
164  if (!is_traceback_matrix || !_transpose)
165  return entry;
166 
167  if constexpr(is_traceback_matrix)
168  {
169  trace_directions reverse{};
170  if ((entry & trace_directions::left) == trace_directions::left)
171  reverse |= trace_directions::up;
172  if ((entry & trace_directions::up) == trace_directions::up)
173  reverse |= trace_directions::left;
174  if ((entry & trace_directions::diagonal) == trace_directions::diagonal)
175  reverse |= trace_directions::diagonal;
176  return reverse;
177  }
178  }
179 
180  if constexpr(is_traceback_matrix)
181  return trace_directions::none;
182  else
183  return std::nullopt;
184  }
185 
192  debug_matrix & mask_matrix(row_wise_matrix<bool> masking_matrix) noexcept
193  {
194  _masking_matrix = masking_matrix;
195  return *this;
196  }
197 
202  debug_matrix & mask_matrix(std::vector<bool> masking_vector) noexcept
203  {
204  return mask_matrix(row_wise_matrix<bool>{masking_vector, rows(), cols()});
205  }
206 
212  debug_matrix & sub_matrix(size_t const new_rows, size_t const new_cols) noexcept
213  {
214  assert(new_rows <= _matrix.rows());
215  assert(new_cols <= _matrix.cols());
216  _rows = new_rows;
217  _cols = new_cols;
218  return *this;
219  }
220 
224  debug_matrix & transpose_matrix() noexcept
225  {
226  _transpose = !_transpose;
227  return *this;
228  }
229 
230 protected:
232  struct format_type; // forward declaration
234 
235 public:
245  template <typename ostream_t>
246  void print(ostream_t & cout, fmtflags2 const flags) const noexcept
247  {
248  format_type const & symbols = (flags & fmtflags2::utf8) == fmtflags2::utf8 ? unicode : csv;
249  size_t const column_width = this->column_width.has_value() ?
250  this->column_width.value() : auto_column_width(flags);
251 
252  auto char_first_sequence = [&]([[maybe_unused]] size_t const i) -> std::string
253  {
254  if constexpr(!has_first_sequence)
255  return " ";
256  else
257  return as_string(first_sequence()[i], flags);
258  };
259 
260  auto char_second_sequence = [&]([[maybe_unused]] size_t const i) -> std::string
261  {
262  if constexpr(!has_second_sequence)
263  return " ";
264  else
265  return as_string(second_sequence()[i], flags);
266  };
267 
268  auto print_cell = [&](std::string const & symbol)
269  {
270  // deal with unicode chars that mess up std::setw
271  size_t const length_bytes = symbol.size();
272  size_t const length = unicode_str_length(symbol);
273  size_t const offset = length_bytes - length;
274 
275  cout << std::left
276  << std::setw(column_width + offset)
277  << symbol
278  << symbols.col_sep;
279  };
280 
281  auto print_first_cell = [&](std::string const & symbol)
282  {
283  cout << symbol << symbols.col_sep;
284  };
285 
286  // |_|d|a|t|a|b|a|s|e|
287  auto print_first_row = [&]
288  {
289  print_first_cell(" ");
290  print_cell(symbols.epsilon);
291 
292  for (size_t col = 0; col < cols() - 1; ++col)
293  print_cell(char_first_sequence(col));
294 
295  cout << "\n";
296  };
297 
298  // |-|-|-|-|-|-|-|-|-|
299  auto print_divider = [&]
300  {
301  cout << " " << symbols.row_col_sep;
302  for (size_t col = 0; col < cols(); ++col)
303  {
304  for (size_t i = 0; i < column_width; ++i)
305  cout << symbols.row_sep;
306 
307  cout << symbols.row_col_sep;
308  }
309  cout << "\n";
310  };
311 
312  print_first_row();
313  for (size_t row = 0; row < rows(); ++row)
314  {
315  if (symbols.row_sep[0] != '\0')
316  print_divider();
317 
318  // one query letter + one row of scores / traces
319  if (row == 0)
320  print_first_cell(symbols.epsilon);
321  else
322  print_first_cell(char_second_sequence(row - 1));
323 
324  for (size_t col = 0; col < cols(); ++col)
325  print_cell(entry_at(row, col, flags));
326 
327  cout << "\n";
328  }
329  }
330 
332  size_t auto_column_width(fmtflags2 const flags) const noexcept
333  {
334  size_t col_width = 1;
335  for (size_t row = 0; row < rows(); ++row)
336  for (size_t col = 0; col < cols(); ++col)
337  col_width = std::max(col_width, unicode_str_length(entry_at(row, col, flags)));
338 
339  return col_width;
340  }
341 
342 protected:
344  std::string entry_at(size_t const row, size_t const col, fmtflags2 flags) const noexcept
345  {
346  format_type const & symbols = (flags & fmtflags2::utf8) == fmtflags2::utf8 ? unicode : csv;
347 
348  entry_type const & entry = at(row, col);
349  if (!is_traceback_matrix && entry == matrix_inf<entry_type>)
350  return symbols.inf;
351 
352  return as_string(entry, flags);
353  }
354 
356  template <typename entry_type>
357  static std::string as_string(entry_type && entry, fmtflags2 const flags) noexcept
358  {
359  std::stringstream strstream;
360  debug_stream_type stream{strstream};
361  stream << flags << entry;
362  return strstream.str();
363  }
364 
367  static size_t unicode_str_length(std::string const & str) noexcept
368  {
369  size_t length = 0u;
370  for (auto it = str.cbegin(); it < str.cend(); ++it, ++length)
371  {
372  uint8_t v = *it;
373  if ((v & 0b11100000) == 0b11000000)
374  ++it;
375  else if ((v & 0b11110000) == 0b11100000)
376  it += 2;
377  else if ((v & 0b11111000) == 0b11110000)
378  it += 3;
379  }
380  return length;
381  }
382 
384  struct format_type
385  {
387  char const * epsilon{};
389  char const * col_sep{};
391  char const * row_sep{};
393  char const * row_col_sep{};
395  char const * inf{};
396  };
397 
399  static constexpr format_type csv{" ", ";", "", "", ""};
401  static constexpr format_type unicode{"ε", "║", "═", "╬", "∞"};
402 
403 public:
405  std::optional<size_t> column_width{std::nullopt};
406 
407 protected:
409  matrix_t _matrix;
411  first_sequence_t _first_sequence;
413  second_sequence_t _second_sequence;
415  std::optional<size_t> _rows{};
417  std::optional<size_t> _cols{};
419  std::optional<row_wise_matrix<bool>> _masking_matrix{};
421  bool _transpose{};
422 };
423 
428 template <Matrix matrix_t>
430 debug_matrix(matrix_t &&)
431  -> debug_matrix<matrix_t>;
432 
435 template <Matrix matrix_t, typename first_sequence_t, typename second_sequence_t>
436 debug_matrix(matrix_t &&, first_sequence_t &&, second_sequence_t &&)
437  -> debug_matrix<matrix_t, first_sequence_t, second_sequence_t>;
439 
440 } // namespace seqan3::detail
441 
442 namespace seqan3
443 {
454 template <detail::Matrix alignment_matrix_t>
455 inline debug_stream_type & operator<<(debug_stream_type & s, alignment_matrix_t && matrix)
456 {
457  detail::debug_matrix debug{std::forward<alignment_matrix_t>(matrix)};
458 
459  std::stringstream sstream{};
460  debug.print(sstream, s.flags2());
461  s << sstream.str();
462  return s;
463 }
464 
465 } // namespace seqan3
Provides seqan3::detail::row_wise_matrix.
debug_stream_type & operator<<(debug_stream_type &s, alignment_matrix_t &&matrix)
An alignment matrix can be printed to the seqan3::debug_stream.
Definition: debug_matrix.hpp:455
Enables use of non-ASCII UTF8 characters in formatted output.
Definition: debug_stream.hpp:40
constexpr auto reverse
A range adaptor that presents the underlying range in reverse order.
Definition: ranges:721
T left(T... args)
SeqAn specific customisations in the standard namespace.
The main SeqAn3 namespace.
fmtflags2 flags2() const
Retrieve the format flags from the stream.
Definition: debug_stream.hpp:203
T setw(T... args)
T str(T... args)
Provides the declaration of seqan3::detail::trace_directions.
T max(T... args)
fmtflags2
Flags that change the behaviour of the seqan3::debug_stream.
Definition: debug_stream.hpp:37
Definition: aligned_sequence_concept.hpp:35
A "pretty printer" for most SeqAn data structures and related types.
Definition: debug_stream.hpp:78
T forward(T... args)
Provides seqan3::detail::Matrix.
Provides seqan3::debug_stream and related types.