A toolkit for working with phylogenetic data.
v0.24.0
utils/containers/matrix/writer.hpp
Go to the documentation of this file.
1 #ifndef GENESIS_UTILS_CONTAINERS_MATRIX_WRITER_H_
2 #define GENESIS_UTILS_CONTAINERS_MATRIX_WRITER_H_
3 
4 /*
5  Genesis - A toolkit for working with phylogenetic data.
6  Copyright (C) 2014-2020 Lucas Czech and HITS gGmbH
7 
8  This program is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21  Contact:
22  Lucas Czech <lucas.czech@h-its.org>
23  Exelixis Lab, Heidelberg Institute for Theoretical Studies
24  Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
25 */
26 
37 
38 #include <cassert>
39 #include <functional>
40 #include <stdexcept>
41 #include <sstream>
42 #include <string>
43 #include <vector>
44 
45 namespace genesis {
46 namespace utils {
47 
48 // =================================================================================================
49 // MatrixWriter
50 // =================================================================================================
51 
52 template <typename T>
54 {
55 public:
56 
57  // -------------------------------------------------------------
58  // Typedefs and Enums
59  // -------------------------------------------------------------
60 
61  enum class Format
62  {
63  kMatrix,
64  kList,
66  };
67 
68  // -------------------------------------------------------------
69  // Constructors and Rule of Five
70  // -------------------------------------------------------------
71 
72  explicit MatrixWriter( std::string const& separator = "\t", Format format = Format::kMatrix )
73  : separator_(separator)
74  , format_(format)
75  {}
76 
77  ~MatrixWriter() = default;
78 
79  MatrixWriter(MatrixWriter const&) = default;
80  MatrixWriter(MatrixWriter&&) = default;
81 
82  MatrixWriter& operator= (MatrixWriter const&) = default;
83  MatrixWriter& operator= (MatrixWriter&&) = default;
84 
85  // -------------------------------------------------------------
86  // Reading
87  // -------------------------------------------------------------
88 
102  void write(
103  Matrix<T> const& matrix,
104  std::shared_ptr<utils::BaseOutputTarget> target,
105  std::vector<std::string> row_names = {},
106  std::vector<std::string> col_names = {},
107  std::string corner = ""
108  ) const {
109  if( row_names.size() == matrix.rows() + 1 ) {
110  if( corner.empty() ) {
111  corner = row_names[0];
112  row_names.erase( row_names.begin() );
113  } else {
114  throw std::runtime_error( "Number of row names is different from Matrix row size." );
115  }
116  } else if( col_names.size() == matrix.cols() + 1 ) {
117  if( corner.empty() ) {
118  corner = col_names[0];
119  col_names.erase( col_names.begin() );
120  } else {
121  throw std::runtime_error( "Number of col names is different from Matrix col size." );
122  }
123  }
124  to_stream_( matrix, target->ostream(), row_names, col_names, corner );
125  }
126 
127  // -------------------------------------------------------------
128  // Properties
129  // -------------------------------------------------------------
130 
131  std::string const& separator_string() const
132  {
133  return separator_;
134  }
135 
136  MatrixWriter& separator_string( std::string const& value )
137  {
138  separator_ = value;
139  return *this;
140  }
141 
142  Format format() const
143  {
144  return format_;
145  }
146 
148  {
149  format_ = value;
150  return *this;
151  }
152 
153  MatrixWriter& write_value_functor( std::function<std::string( T const& )> functor )
154  {
155  write_value_ = functor;
156  }
157 
158  // -------------------------------------------------------------
159  // Internal Functions
160  // -------------------------------------------------------------
161 
162 private:
163 
164  void to_stream_(
165  Matrix<T> const& mat,
166  std::ostream& os,
167  std::vector<std::string> const& row_names,
168  std::vector<std::string> const& col_names,
169  std::string const& corner
170  ) const {
171 
172  // Checks.
173  if( ! row_names.empty() && row_names.size() != mat.rows() ) {
174  throw std::invalid_argument( "Number of row names is different from Matrix row size." );
175  }
176  if( ! col_names.empty() && col_names.size() != mat.cols() ) {
177  throw std::invalid_argument( "Number of col names is different from Matrix col size." );
178  }
179 
180  // Format switch
181  switch( format_ ) {
182  case Format::kMatrix: {
183  to_matrix_( mat, os, row_names, col_names, corner );
184  break;
185  }
186  case Format::kList: {
187  to_list_( mat, os, row_names, col_names );
188  break;
189  }
190  case Format::kTriangular: {
191  to_triangular_( mat, os, row_names, col_names, corner );
192  break;
193  }
194  default: {
195  throw std::invalid_argument( "Invalid enum value for MatrixWriter::Format" );
196  }
197  }
198  }
199 
200  void to_matrix_(
201  Matrix<T> const& mat,
202  std::ostream& os,
203  std::vector<std::string> const& row_names,
204  std::vector<std::string> const& col_names,
205  std::string const& corner
206  ) const {
207 
208  // Write top corner cell if needed.
209  if( ! row_names.empty() && ! col_names.empty() ) {
210  os << corner << separator_;
211  }
212 
213  // Write col names.
214  if( ! col_names.empty() ) {
215  for( size_t c = 0; c < col_names.size(); ++c ) {
216  if( c > 0 ) {
217  os << separator_;
218  }
219  os << col_names[c];
220  }
221  os << "\n";
222  }
223 
224  // Write lines.
225  for( size_t r = 0; r < mat.rows(); ++r ) {
226  if( ! row_names.empty() ) {
227  os << row_names[r] << separator_;
228  }
229 
230  for( size_t c = 0; c < mat.cols(); ++c ) {
231  if( c > 0 ) {
232  os << separator_;
233  }
234  if( write_value_ ) {
235  os << write_value_( mat( r, c ));
236  } else {
237  os << mat( r, c );
238  }
239  }
240 
241  os << "\n";
242  }
243  }
244 
245  void to_list_(
246  Matrix<T> const& mat,
247  std::ostream& os,
248  std::vector<std::string> const& row_names,
249  std::vector<std::string> const& col_names
250  ) const {
251 
252  // Simple: one line per cell
253  for( size_t r = 0; r < mat.rows(); ++r ) {
254  for( size_t c = 0; c < mat.cols(); ++c ) {
255  if( ! row_names.empty() ) {
256  os << row_names[r] << separator_;
257  }
258  if( ! col_names.empty() ) {
259  os << col_names[c] << separator_;
260  }
261  os << mat( r, c );
262  os << "\n";
263  }
264  }
265  }
266 
267  void to_triangular_(
268  Matrix<T> const& mat,
269  std::ostream& os,
270  std::vector<std::string> const& row_names,
271  std::vector<std::string> const& col_names,
272  std::string const& corner
273  ) const {
274 
275  // Check
276  if( mat.rows() != mat.cols() ) {
277  throw std::invalid_argument( "Cannot write triangular matrix, as it is not quadratic." );
278  }
279 
280  // Write top corner cell if needed.
281  if( ! row_names.empty() && ! col_names.empty() ) {
282  os << corner << separator_;
283  }
284 
285  // Write col names backwards.
286  if( ! col_names.empty() ) {
287  for( size_t cb = 0; cb < col_names.size(); ++cb ) {
288  auto const c = col_names.size() - cb - 1;
289  if( cb > 0 ) {
290  os << separator_;
291  }
292  os << col_names[c];
293  }
294  os << "\n";
295  }
296 
297  // Write lines, with backwards columns to get a nice looking triangluar matrix.
298  for( size_t r = 0; r < mat.rows(); ++r ) {
299  if( ! row_names.empty() ) {
300  os << row_names[r] << separator_;
301  }
302 
303  // Write the trinangular columns, starting at col = row
304  for( size_t cb = 0; cb < mat.cols() - r; ++cb ) {
305  auto const c = mat.cols() - cb - 1;
306  assert( c < mat.cols() );
307 
308  if( cb > 0 ) {
309  os << separator_;
310  }
311  if( write_value_ ) {
312  os << write_value_( mat( r, c ));
313  } else {
314  os << mat( r, c );
315  }
316  }
317 
318  os << "\n";
319  }
320  }
321 
322  // -------------------------------------------------------------
323  // Data Members
324  // -------------------------------------------------------------
325 
326 private:
327 
328  std::string separator_ = std::string( "\t" );
329  Format format_ = Format::kMatrix;
330 
331  std::function<std::string( T const& )> write_value_;
332 
333 };
334 
335 } // namespace utils
336 } // namespace genesis
337 
338 #endif // include guard
MatrixWriter(std::string const &separator="\, Format format=Format::kMatrix)
MatrixWriter & write_value_functor(std::function< std::string(T const &)> functor)
Container namespace for all symbols of genesis in order to keep them separate when used as a library...
Provides some valuable additions to STD.
MatrixWriter & operator=(MatrixWriter const &)=default
std::string const & separator_string() const
MatrixWriter & separator_string(std::string const &value)
void write(Matrix< T > const &matrix, std::shared_ptr< utils::BaseOutputTarget > target, std::vector< std::string > row_names={}, std::vector< std::string > col_names={}, std::string corner="") const
Write a Matrix to an output target, using a specific MatrixWriter::Format and separator string...