C++-Programmierung/ Eine Matrix-Bibliothek – mitrax/ io.hpp

#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