#ifndef _mitrax_mitrax_io_hpp_INCLUDED_
#define _mitrax_mitrax_io_hpp_INCLUDED_
/// \file io.hpp
///
/// \brief Ein- und Ausgabe
///
/// Diese Datei stellt die Ein- und Ausgabe für Matrizen und Proxys bereit. Es sind beliebige
/// <code>std::basic_ostream</code>-Objekte für die Ausgabe und beliebige
/// <code>std::basic_istream</code>-Objekte für die Eingabe zugelassen.
///
/// \section io_format Format
///
/// Die beiden Formate für Matrizen und Proxys bestehen aus einer Reihe von Token. Ein Token kann
/// dabei ein Nicht-Whitespace-Zeichen, eine ganze Zahl oder ein Element sein. Die Token können
/// durch beliebig viele Whitespace-Zeichen getrennt sein. Auf das Format der Elemente hat mitrax
/// keinen Einfluss.
///
/// \subsection matrix_format Format für Matrizen
///
/// Das Matrixformat beginnt mit eckigen Klammern, in welchen die Anzahl der Zeilen und die Anzahl
/// der Spalten durch Komma getrennt angegeben sind. Anschließend folgt eine durch Kommata
/// separierte und in runden Klammern eingeschlossene Liste gleichlanger Zeilen. Eine Zeile besteht
/// aus einer kommaseparierten, in runden Klammern eingeschossenen Liste von Elementen.
///
/// \em Beispiel: <code>[2,3]((1,2,3),(4,5,6))</code>
///
/// Eine Matrix mit zwei Zeilen und drei Spalten, welche in der ersten Zeile die Elemente 1, 2 und
/// 3 besitze und in der zweiten Zeile die Elemente 4, 5 und 6.
///
/// Für die Eingabe muss das Matrixobjekt nicht über über die gleiche Dimension verfügen, wie die
/// einzulesende Matrix.
///
/// \subsection proxy_format Format für Proxys
///
/// Das Format für Proxys unterscheidet nicht zwischen Zeilen- und Spaltenproxys. Es beginnt mit
/// eckigen Klammern, welche die Anzahl der Elemente enthalten. Diese muss mit der Anzahl der
/// Elemente in der Zeile bzw. Spalte, in die eingelesen werden soll übereinstimmen. Anschließend
/// folgt in runden Klammern eingeschlossen und durch Kommata separiert die Liste der Elemente.
///
/// \em Beispiel: <code>[3](3.142, 2,718, 1,414)</code>
///
/// Eine Zeile oder Spalte mit drei Elementen. Zur besseren Übersicht wurde hinter den Komma noch
/// ein Leerzeichen eingefügt.
#include <istream>
#include <ostream>
#include <sstream>
#include <iomanip>
#include <vector>
#include "iomanip.hpp"
#include "matrix.hpp"
namespace mitrax{
namespace detail{
/// \brief Hilfsfunktion für die Ausgabe einer Zeile oder Spalte
template < class charT, class traits, typename Iterator, typename Size >
inline
std::basic_ostream< charT, traits >&
proxy_output(
std::basic_ostream< charT, traits >& os,
Iterator iter,
Size const& size
){
// Ausgabe erfolgt zunächst auf Puffer
std::basic_ostringstream< charT, traits, std::allocator< charT > > tos;
tos.flags(os.flags());
tos.imbue(os.getloc());
tos.precision(os.precision());
// Manipulatoren
long const output_width = get_element_width_flag(os);
bool const multi_line = get_multi_line_flag(os);
// Ausgabe Header
tos << '[' << size << ']';
if(multi_line) tos << '\n';
// Ausgabe der Zeile / Spalte
tos << '(';
if(size){
tos << std::setw(output_width) << *iter;
++iter;
}
for(Size i = Size()+1; i < size; ++i){
tos << ',' << std::setw(output_width) << *iter;
++iter;
}
tos << ')';
// Ausgabe auf Stream und Streamrückgabe
return os << tos.str().c_str();
}
/// \brief Prüft ob die nächste Eingabe der Vorgabe entspricht
template < class charT, class traits, class ShouldBe >
inline
bool
input_equal(
std::basic_istream< charT, traits >& is,
ShouldBe const& should_be
){
ShouldBe in;
if(is >> in && in == should_be){
return true;
}
is.setstate(std::ios_base::failbit);
return false;
}
/// \brief Prüft ob das nächste Nicht-Whitespaces-Zeichen der Vorgabe entspricht
template < class charT, class traits >
inline
bool
input_equal(
std::basic_istream< charT, traits >& is,
charT const& should_be
){
if(!is) return false;
charT in;
if(is >> in && in == should_be) return true;
is.putback(in);
is.setstate(std::ios_base::failbit);
return false;
}
/// \brief Helferfunktion für die Eingabe einer Zeile oder Spalte
template < class charT, class traits, typename Iterator, typename Size >
inline
std::basic_istream< charT, traits >&
proxy_input(
std::basic_istream< charT, traits >& is,
Iterator iter,
Size const& size
){
typedef std::vector< typename Iterator::value_type > container_type;
// Header einlesen und Länge prüfen
if(
!input_equal(is, charT('[')) ||
!input_equal(is, size) ||
!input_equal(is, charT(']')) ||
!input_equal(is, charT('('))
) return is;
// Werte zunächst komplett in temporären Container einlesen
container_type elements(size);
typename container_type::iterator tmp_iter = elements.begin();
for(Size i = Size(); i < size-1; ++i){
if(is >> *tmp_iter++ && input_equal(is, charT(','))) continue;
return is;
}
if(
!(is >> *tmp_iter++) ||
!input_equal(is, charT(')'))
) return is;
// Eingelesene Werte übertragen
tmp_iter = elements.begin();
for(Size i = Size(); i < size; ++i){
*iter = *tmp_iter;
++tmp_iter;
++iter;
}
return is;
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Output
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/// \brief Zeile einer konstanten Matrix ausgeben
template < class charT, class traits, typename Matrix >
inline
std::basic_ostream< charT, traits >&
operator<<(
std::basic_ostream< charT, traits >& os,
row_const_proxy< Matrix > const& proxy
){
return detail::proxy_output(os, proxy.cbegin(), proxy.columns());
}
/// \brief Spalte einer konstanten Matrix ausgeben
template < class charT, class traits, typename Matrix >
inline
std::basic_ostream< charT, traits >&
operator<<(
std::basic_ostream< charT, traits >& os,
column_const_proxy< Matrix > const& proxy
){
return detail::proxy_output(os, proxy.cbegin(), proxy.rows());
}
/// \brief Zeile einer Matrix ausgeben
template < class charT, class traits, typename Matrix >
inline
std::basic_ostream< charT, traits >&
operator<<(
std::basic_ostream< charT, traits >& os,
row_proxy< Matrix > const& proxy
){
return os << row_const_proxy< Matrix >(proxy);
}
/// \brief Spalte einer Matrix ausgeben
template < class charT, class traits, typename Matrix >
inline
std::basic_ostream< charT, traits >&
operator<<(
std::basic_ostream< charT, traits >& os,
column_proxy< Matrix > const& proxy
){
return os << column_const_proxy< Matrix >(proxy);
}
/// \brief Matrix ausgeben
template < class charT, class traits, typename T, typename Container >
inline
std::basic_ostream< charT, traits >&
operator<<(
std::basic_ostream< charT, traits >& os,
matrix< T, Container > const& m
){
typedef typename matrix< T, Container >::size_type size_type;
typedef typename matrix< T, Container >::const_iterator const_iterator;
// Ausgabe erfolgt zunächst auf Puffer
std::basic_ostringstream< charT, traits, std::allocator< charT > > tos;
tos.flags(os.flags());
tos.imbue(os.getloc());
tos.precision(os.precision());
// Manipulatoren
long const output_width = get_element_width_flag(os);
bool const multi_line = get_multi_line_flag(os);
// Schneller Zugriff
size_type const rows = m.rows();
size_type const columns = m.columns();
// Elementweise durchgehen mittels Iterator
const_iterator iter = m.begin();
// Ausgabe Header
tos << '[' << rows << ',' << columns << "](";
if(multi_line) tos << '\n';
// Ausgabe erste Zeile
if(rows){
tos << '(';
// Ausgabe erstes Element
if(columns){
tos << std::setw(output_width) << *iter++;
}
// Ausgabe restliche Element
for(typename matrix< T, Container >::size_type j = 1; j < columns; ++j){
tos << ',' << std::setw(output_width) << *iter++;
}
tos << ')';
}
// Ausgabe restliche Zeilen
for(typename matrix< T, Container >::size_type i = 1; i < rows; ++i){
tos << ',';
if(multi_line) tos << '\n';
tos << '(';
// Ausgabe erstes Element
if(columns){
tos << std::setw(output_width) << *iter++;
}
// Ausgabe restliche Element
for(typename matrix< T, Container >::size_type j = 1; j < columns; ++j){
tos << ',' << std::setw(output_width) << *iter++;
}
tos << ')';
}
if(multi_line) tos << '\n';
tos << ')';
// Ausgabe auf Stream und Streamrückgabe
return os << tos.str().c_str();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Input
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/// \brief Matrix einlesen
template < class charT, class traits, typename T, typename Container >
inline
std::basic_istream< charT, traits >&
operator>>(
std::basic_istream< charT, traits >& is,
matrix< T, Container >& m
){
using detail::input_equal;
typedef typename matrix< T, Container >::size_type size_type;
typedef typename matrix< T, Container >::const_iterator const_iterator;
typedef std::vector< T > container_type;
// Neue Dimension der Matrix
size_type rows;
size_type columns;
// Header einlesen
if(
!input_equal(is, charT('[')) ||
!(is >> rows) ||
!input_equal(is, charT(',')) ||
!(is >> columns) ||
!input_equal(is, charT(']')) ||
!input_equal(is, charT('('))
) return is;
// Daten erst komplett Einlesen und dann gegen Originale tauschen
container_type elements(rows * columns);
typename container_type::iterator tmp_iter = elements.begin();
// Lese n-1 Zeilen
for(size_type i = size_type(); i < rows-1; ++i){
if(
!input_equal(is, charT('('))
) return is;
// Lese n-1 Spalten
for(size_type j = size_type(); j < columns-1; ++j){
if(is >> *tmp_iter++ && input_equal(is, charT(','))) continue;
return is;
}
if( // Lese n-te Spalten
!(is >> *tmp_iter++) ||
!input_equal(is, charT(')')) ||
!input_equal(is, charT(','))
) return is;
}
// Lese n-te Zeilen
if(
!input_equal(is, charT('('))
) return is;
// Lese n-1 Spalten
for(size_type j = size_type(); j < columns-1; ++j){
if(is >> *tmp_iter++ && input_equal(is, charT(','))) continue;
return is;
}
if( // Lese n-te Spalten
!(is >> *tmp_iter++) ||
!input_equal(is, charT(')')) ||
!input_equal(is, charT(')'))
) return is;
// Eingelesene Werte gegen Matrixwerte tauschen
m.reinit(rows, columns, elements);
return is;
}
/// \brief Zeile einer Matrix einlesen
template < class charT, class traits, typename Matrix >
inline
std::basic_istream< charT, traits >&
operator>>(
std::basic_istream< charT, traits >& is,
row_proxy< Matrix > proxy
){
return detail::proxy_input(is, proxy.begin(), proxy.columns());
}
/// \brief Spalte einer Matrix einlesen
template < class charT, class traits, typename Matrix >
inline
std::basic_istream< charT, traits >&
operator>>(
std::basic_istream< charT, traits >& is,
column_proxy< Matrix > proxy
){
return detail::proxy_input(is, proxy.begin(), proxy.rows());
}
}
#endif