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

#ifndef _mitrax_mitrax_matrix_hpp_INCLUDED_
#define _mitrax_mitrax_matrix_hpp_INCLUDED_
/// \file matrix.hpp
///
/// \brief Matrixklasse
///
/// In dieser Datei steht die Definition der <code>matrix</code>-Klasse, welche die Kernkomponente
/// von mitrax bildet. Eine Überladung der <code>swap()</code>-Funktion für
/// <code>matrix</code>-Objekte ist ebenfalls hier definiert. Weiterhin existiert in dieser Datei
/// die von mitrax genutzter Trait-Klasse <code>identity</code>, welche für einen gegeben Datentyp
/// die neutralen Elemente bezüglich der Addition und Multiplikation liefert.


#include <vector>
#include "proxy.hpp"
#include "dimension.hpp"

namespace mitrax{

namespace detail{

/// \brief Positionsberechnung eines Elements in einem 1D-Feld anhand der 2D-Position
///
/// Diese Klasse sollte nicht direkt vom Nutzer verwendet werden.
///
/// siehe matrix::position_adapter
template < typename SizeType >
class position_adapter{
public:
    /// \brief Nutzt <code>columns</code> als Spaltenanzahl
    position_adapter(SizeType columns):columns_(columns){}
    /// \brief Nutzt <code>dim.columns()</code> als Spaltenanzahl
    position_adapter(dimension< SizeType > const& dim):columns_(dim.columns()){}

    /// \brief Gibt <code>row * Spaltenanzahl + column</code> zurück
    SizeType const operator()(SizeType const& row, SizeType const& column){
        return row * columns_ + column;
    }

private:
    /// \brief Gespeicherte Spaltenanzahl
    SizeType columns_;
};

}

/// \brief Matrizen mit Elementzugriff und Größenanpassung
///
/// Die <code>matrix</code>-Templateklasse ist das Herzstück von mitrax. Sie besitzt im
/// Wesentlichen eine Dimension (Zeilen mal Spalten) und die entsprechend angeordneten Element.
///
/// Die Erstellung einer Matrix kann mittels Standardkonstruktor mit einer Dimension von Null
/// erfolgen. Weiterhin existieren Konstruktoren für die Erstellung mit einer angegebenen Dimension
/// und einem Füllelement (Defaultwert T()) oder einem Argument vom Typ Referenz auf Container,
/// welches die korrekte Anzahl von Elementen enthalten muss. Das Argument wird nach dem Aufruf
/// keine Elemente mehr enthalten!
///
/// Compilergenerierte Methoden:
///   - Kopierkonstruktor
///     - T muss kopierkonstruierbar sein
///     - Container muss kopierkonstruierbar sein
///     - size_type muss kopierkonstruierbar sein
///   - Kopierzuweisung
///     - T muss kopierzuweisbar sein
///     - Container muss kopierzuweisbar sein
///     - size_type muss kopierzuweisbar sein
///   - Desturktor
///     - T muss destruierbar sein
///     - Container muss destruierbar sein
///     - size_type muss destruierbar sein
///
/// \section proxy_interface Proxy-Interface
///
/// Der wahlfreie Zugriff auf einzelne Elemente erfolgt, unter Zuhilfenahme von Proxyklassen, durch
/// zweimalige Anwendung des Indexoperators (<code>operator[]()</code>). Der Zugriff kann auch über
/// Methoden <code>row()</code> und <code>column()</code> erfolgen. Entsprechend ist auch der
/// Zugriff auf einzelne Zeilen und Spalten möglich. Der Zugriff über Zeilenproxys ist tendenziell
/// schneller.
///
/// Die Proxyklassen können kopiert und aneinander zugewiesen werden, so dass unter bestimmten
/// Umständen, die Vertauschung zweier Proxys stellvertretend für die Vertauschung zweier
/// kompletter Zeilen oder Spalten genutzt werden kann. Zu diesem Zweck kann beispielsweise ein
/// Feld mit allen Zeilenproxys einer Matrix erstellt werden. Der Zugriff auf die Elemente ist
/// dann, durch zweimalige Anwendung des Indexoperators, auch über dieses Feld möglich.
///
/// Um Zuweisungen an ein temporäres Objekt zu verhindern, sind die von matrix zurückgegebenen
/// Proxyobjekte immer konstant. Die Daten, auf welche die Proxys verweisen, haben mit dieser
/// Konstantheit natürlich nichts zu tun.
///
/// \section iterator_interface Iterator-Interface
///
/// Für die elementweise Bearbeitung der Elemente wird ein Iterator-Interface angeboten. Neben den
/// Methoden <code>begin()</code> und <code>end()</code>, welche in einer konstanten und einer
/// nicht-konstante Version existieren, gibt es auch noch die Methoden <code>cbegin()</code> und
/// <code>cend()</code>, welche immer <code>const_iterator</code>-Objekte zurückgeben.
///
/// Auch die Proxyklassen verfügen über ein Iteratorinterface dieser Art. Wobei zu beachten ist,
/// das die Methoden <code>begin()</code> und <code>end()</code> für die Proxys für nicht-konstante
/// Matrizen (<code>row_proxy</code> und <code>column_proxy</code>) immer auch Iteratoren für
/// nicht-konstante Matrizen zurückgeben, unabhängig davon, ob das Proxyobjekt selbst konstant ist.
template < typename T, typename Container = std::vector< T > >
class matrix{
public:
    // types:
    /// \brief Datentyp der Elemente
    typedef T                                     value_type;
    /// \brief Referenz auf ein Element
    typedef T&                                    reference;
    /// \brief Referenz auf ein konstantes Element
    typedef T const&                              const_reference;
    /// \brief Typ des Containers
    typedef Container                             container_type;

    /// \brief Typ für eine Anzahl Zeilen oder Spalten
    typedef typename Container::size_type         size_type;
    /// \brief Typ der Dimension
    typedef mitrax::dimension< size_type >        dimension_type;

    /// \brief Iteratortyp der auf den <code>value_type</code> verweist
    typedef typename Container::iterator          iterator;
    /// \brief Iteratortyp der auf den konstanten <code>value_type</code> verweist
    typedef typename Container::const_iterator    const_iterator;
    /// \brief Vorzeichenbehaftete Ganzzahl
    typedef typename Container::difference_type   difference_type;

    /// \brief Proxy für eine Zeile
    typedef mitrax::row_proxy< matrix >           row_proxy;
    /// \brief Proxy für eine konstante Zeile
    typedef mitrax::row_const_proxy< matrix >     row_const_proxy;
    /// \brief Proxy für eine Spalte
    typedef mitrax::column_proxy< matrix >        column_proxy;
    /// \brief Proxy für eine konstante Spalte
    typedef mitrax::column_const_proxy< matrix >  column_const_proxy;

    /// \brief Positionsberechnung eines Elements in einem 1D-Feld anhand der 2D-Position
    ///
    /// Es handelt sich bei diesem Typ um einen Funktor, welcher anhand einer Zeilenlänge und
    /// zweier 2D-Koordinaten die Speicherposition eines Elements, in einem eindimensionalen
    /// Container berechnet. Er bekommt bei seiner Initialisierung die Anzahl der Spalten
    /// (Zeilenlänge) und kann anschließend mit den zwei Koordinaten (x, y) aufgerufen werden.
    ///
    /// Zur Initialisierung kann neben einer Zeilenlänge auch ein <code>dimension</code>-Objekt
    /// verwendet werden.
    typedef detail::position_adapter< size_type > position_adapter;


    // construct/copy/destroy:
    /// \brief Erstellt eine Matrix mit Null Zeilen und Spalten
    matrix();
    /// \brief Erstellt eine Matrix der Dimension <code>size</code> und füllt alle Elemente mit
    ///        <code>fill</code>
    matrix(dimension_type const& size, const_reference fill = value_type());
    /// \brief Erstellt eine Matrix mit <code>rows</code> Zeilen und <code>columns</code> Spalten
    ///        und füllt alle Elemente mit <code>fill</code>
    matrix(size_type const& rows, size_type const& columns, const_reference fill = value_type());
    /// \brief Erstellt eine Matrix der Dimension <code>size</code> und verwendet <code>data</code>
    ///        als Elemente
    ///
    /// In <code>data</code> müssen <code>size.rows() * size.columns()</code> viele Einträge
    /// enthalten sein. Der Konstruktor tauscht <code>data</code> gegen einen leeren Container aus.
    /// Die Berechnung der zweidimensionalen Position erfolgt mit
    /// <code>row * columns + column</code>. Dies gewährleistet eine sehr effiziente
    /// Initialisierung.
    ///
    /// \throw error::data_size falls <code>data.size() != size.rows() * size.columns()</code>
    matrix(dimension_type const& size, container_type& data);
    /// \brief Erstellt eine Matrix mit rows Zeilen und columns Spalten und verwendet data als
    ///        Elemente
    ///
    /// \throw error::data_size falls <code>data.size() != size.rows() * size.columns()</code>
    matrix(size_type const& rows, size_type const& columns, container_type& data);


    // iterators:
    /// \brief Gibt einen Schreib-/Leseiterator auf das erste Element der Matrix zurück
    ///
    /// Siehe \ref iterator_interface "Iterator-Interface"
    iterator const       begin();
    /// \brief Gibt einen Leseiterator auf das erste Element der Matrix zurück
    ///
    /// Siehe \ref iterator_interface "Iterator-Interface"
    const_iterator const begin()const;
    /// \brief Gibt einen Schreib-/Leseiterator hinter das letzte Element der Matrix zurück
    ///
    /// Siehe \ref iterator_interface "Iterator-Interface"
    iterator const       end();
    /// \brief Gibt einen Leseiterator hinter das letzte Element der Matrix zurück
    ///
    /// Siehe \ref iterator_interface "Iterator-Interface"
    const_iterator const end()const;
    /// \brief Gibt einen Leseiterator auf das erste Element der Matrix zurück
    ///
    /// Siehe \ref iterator_interface "Iterator-Interface"
    const_iterator const cbegin()const;
    /// \brief Gibt einen Leseiterator hinter das letzte Element der Matrix zurück
    ///
    /// Siehe \ref iterator_interface "Iterator-Interface"
    const_iterator const cend()const;


    // capacity:
    /// \brief Gibt die Dimension der Matrix zurück
    ///
    /// Die Dimension einer Matrix beinhaltet die Anzahl der Zeilen und Spalten.
    ///
    /// Siehe auch:
    ///   - \link rows \endlink
    ///   - \link columns \endlink
    dimension_type const dimension()const;

    /// \brief Gibt die Anzahl der Zeilen zurück
    ///
    /// Siehe auch:
    ///   - \link rows \endlink
    ///   - \link dimension \endlink
    size_type const rows()const;
    /// \brief Gibt die Anzahl der Spalten zurück
    ///
    /// Siehe auch:
    ///   - \link columns \endlink
    ///   - \link dimension \endlink
    size_type const columns()const;

    /// \brief Setzt die Dimension der Matrix neu
    ///
    /// Diese Methode setzt die Dimension der Matrix neu. Ist <code>preserve</code> gesetzt,
    /// werden Elemente die bei der alten und neuen Dimension existieren, erhalten. Alle übrigen
    /// Elemente werden auf <code>fill</code> gesetzt. Ist <code>preserve</code> nicht gesetzt,
    /// werden alle Elemente auf <code>fill</code> gesetzt.
    void resize(
        dimension_type const& size,
        bool preserve = true,
        const_reference fill = value_type()
    );
    /// \brief Setzt die Dimension der Matrix neu
    ///
    /// Siehe \link resize \endlink
    void resize(
        size_type const& rows,
        size_type const& columns,
        bool preserve = true,
        const_reference fill = value_type()
    );

    /// \brief Reinitialisiert die Matrix mit gegebener Dimension und gegebenen Daten
    ///
    /// In <code>data</code> müssen <code>size.rows() * size.columns()</code> viele Einträge
    /// enthalten sein. Der Konstruktor tauscht <code>data</code> gegen den aktuellen Container
    /// mit den Elementdaten aus. Die Berechnung der zweidimensionalen Position erfolgt mit
    /// <code>row * columns + column</code>. Dies gewährleistet eine sehr effiziente
    /// Initialisierung.
    ///
    /// \throw error::data_size falls <code>data.size() != size.rows() * size.columns()</code>
    void reinit(dimension_type const& size, container_type& data);
    /// \brief Reinitiallisiert die Matrix mit gegebener Dimension und gegebenen Daten
    ///
    /// \throw error::data_size falls <code>data.size() != size.rows() * size.columns()</code>
    void reinit(size_type const& rows, size_type const& columns, container_type& data);


    // element access:
    /// \brief Gibt einen Schreib-/Leseproxy für die Zeile <code>row</code> zurück
    row_proxy const          operator[](size_type const& row);
    /// \brief Gibt einen Leseproxy für die Zeile <code>row</code> zurück
    row_const_proxy const    operator[](size_type const& row)const;
    /// \brief Gibt einen Schreib-/Leseproxy für die Zeile <code>number</code> zurück
    row_proxy const          row(size_type const& number);
    /// \brief Gibt einen Leseproxy für die Zeile <code>number</code> zurück
    row_const_proxy const    row(size_type const& number)const;
    /// \brief Gibt einen Schreib-/Leseproxy für die Spalte <code>number</code> zurück
    column_proxy const       column(size_type const& number);
    /// \brief Gibt einen Leseproxy für die Spalte <code>number</code> zurück
    column_const_proxy const column(size_type const& number)const;


    // modifiers:
    /// \brief Löscht eine oder mehrere Zeile
    ///
    /// Durch das Löschen von <code>count</code> vielen Zeilen verringert sich die Anzahl
    /// der Zeilen um <code>count</code>.
    void erase_row(size_type const& number, size_type const& count = size_type(1));
    /// \brief Löscht eine oder mehrere Spalte
    ///
    /// Durch das Löschen von <code>count</code> vielen Spalten verringert sich die Anzahl
    /// der Spalten um <code>count</code>.
    void erase_column(size_type const& number, size_type const& count = size_type(1));

    /// \brief Tauscht Dimension und Werte der aktuellen Matrix mit jenen aus <code>m</code>
    ///
    /// Die Vertauschung erfolgt durch den Aufruf von <code>swap()</code> für die beiden
    /// Containerobjekte und die Dimensionobjekte. Bei ersteren existiert oft eine überladene
    /// oder spezialisierte Version, die sehr Effizient ist. Die argumentabhängige Namenssuche
    /// für eine solche <code>swap()</code>-Funktion wird unterstützt.
    void swap(matrix& m);

private:
    /// \brief Speichert die Anzahl der Zeilen und Spalten
    dimension_type dimension_;
    /// \brief Speichert die Elemente der Matrix
    ///
    /// Der Zugriff im 2D-Raum erfolgt mittels <code>row * columns + column</code>
    container_type data_;

    /// \brief Helfer um Codeverdopplung zu vermeiden
    ///
    /// Prüft ob die Anzahl der Elemente in <code>data</code> zu <code>size</code> passt. Ist dies
    /// nicht der Fall, wird eine Ausnahme vom Typ <code>error::data_size</code> geworfen.
    /// Andernfalls wird <code>swap(data_, data)</code> aufgerufen. Die Dimension wird \em nicht
    /// zugewiesen.
    ///
    /// \throw error::data_size
    void raw_data_swap(dimension_type const& size, container_type& data);
};

/// \brief Vertauscht zwei Matrizen
///
/// Ruft <code>lhs.swap(rhs)</code> auf.
template < typename T, typename Container >
void swap(matrix< T, Container >& lhs, matrix< T, Container >& rhs);

/// \brief Neutrale Elemente für einen numerischen Datentyp <code>T</code>
///
/// Dies ist eine Trait-Klasse. Sie stellt durch die statischen Funktionen <code>additive()</code>
/// und <code>multiplicative()</code> die neutralen Elemente bezüglich der Addition und der
/// Multiplikation mit einem Objekt eines numerischen Datentyps <code>T</code> bereit.
///
/// In der Standardimplementierung wird angenommen, das ein standardkonstruiertes
/// <code>T</code>-Objekt bezüglich der Addition neutral ist. Addiert man zu diesem den
/// <code>int</code>-Literal <code>1</code> sollte sich das neutrale Element bezüglich der
/// Multiplikation ergeben. Trifft dies auf einen Datentyp den Sie verwenden möchten nicht zu, so
/// können Sie eine entsprechende Spezialisierung des Templates  <code>identity</code> vornehmen.
///
/// Standardmäßig gibt es eine Spezialisierung für alle Klassentemplates, die eine Parametertyp
/// <code>U</code> übernehmen. Für derartige Klassen wird wiederum angenommen, das ein
/// standardkonstruiertes Objekt bezüglich der Addition neutral ist. Für die Multiplikation wird
/// diesmal angenommen, dass sich das neutrale Element durch die Addition des standardkonstruierten
/// Objekts mit dem multiplikativen Neutrum von <code>U<code> ergibt.
///
/// Klassen, die mehrere neutrale Elemente pro Operation besitzen, wie etwa die
/// <code>matrix</code>-Klasse, werden derzeit nicht unterstützt.
template < typename T >
struct identity{
    /// \brief Additives Neutrum
    static T const& additive()      { static T const t=T();    return t; };
    /// \brief Multiplikatives Neutrum
    static T const& multiplicative(){ static T const t(T()+1); return t; };
};

/// \brief Neutrale Elemente für einen numerischen Datentyp <code>Numeric< T ></code>
///
/// Speziallisierung von <code>identity< T ></code>.
template < typename T, template < typename U > class Numeric >
struct identity< Numeric< T > >{
    /// \brief Additives Neutrum
    static Numeric< T > const& additive(){
        static Numeric< T > const t=T(); return t;
    };

    /// \brief Multiplikatives Neutrum
    static Numeric< T > const& multiplicative(){
        static Numeric< T > const t(Numeric< T >() + identity< T >::multiplicative());
        return t;
    };
};


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Definitionen
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

template < typename T, typename Container >
inline
matrix< T, Container >::matrix()
    {}

template < typename T, typename Container >
inline
matrix< T, Container >::matrix(
    matrix< T, Container >::dimension_type const& size,
    const_reference fill
):
    dimension_(size),
    data_(elements(dimension_), fill)
    {}

template < typename T, typename Container >
inline
matrix< T, Container >::matrix(
    size_type const& rows,
    size_type const& columns,
    const_reference fill
):
    dimension_(dimension_type(rows, columns)),
    data_(elements(dimension_), fill)
    {}

template < typename T, typename Container >
inline
matrix< T, Container >::matrix(
    dimension_type const& size,
    container_type& data
):
    dimension_(size)
{
    raw_data_swap(dimension_, data);
}

template < typename T, typename Container >
inline
matrix< T, Container >::matrix(
    size_type const& rows,
    size_type const& columns,
    container_type& data
):
    dimension_(dimension_type(rows, columns))
{
    raw_data_swap(dimension_, data);
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::dimension_type const
matrix< T, Container >::dimension()const{
    return dimension_;
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::size_type const
matrix< T, Container >::rows()const{
    return dimension_.rows();
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::size_type const
matrix< T, Container >::columns()const{
    return dimension_.columns();
}


template < typename T, typename Container >
inline
typename matrix< T, Container >::iterator const
matrix< T, Container >::begin(){
    return data_.begin();
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::const_iterator const
matrix< T, Container >::begin()const{
    return data_.begin();
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::iterator const
matrix< T, Container >::end(){
    return data_.end();
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::const_iterator const
matrix< T, Container >::end()const{
    return data_.end();
}


template < typename T, typename Container >
inline
typename matrix< T, Container >::const_iterator const
matrix< T, Container >::cbegin()const{
    return begin();
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::const_iterator const
matrix< T, Container >::cend()const{
    return end();
}


template < typename T, typename Container >
inline
typename matrix< T, Container >::row_proxy const
matrix< T, Container >::operator[](
    size_type const& number
){
    return row(number);
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::row_const_proxy const
matrix< T, Container >::operator[](
    size_type const& number
)const{
    return row(number);
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::row_proxy const
matrix< T, Container >::row(
    size_type const& number
){
    return row_proxy(*this, number);
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::row_const_proxy const
matrix< T, Container >::row(
    size_type const& number
)const{
    return row_const_proxy(*this, number);
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::column_proxy const
matrix< T, Container >::column(
    size_type const& number
){
    return column_proxy(*this, number);
}

template < typename T, typename Container >
inline
typename matrix< T, Container >::column_const_proxy const
matrix< T, Container >::column(
    size_type const& number
)const{
    return column_const_proxy(*this, number);
}


template < typename T, typename Container >
inline
void matrix< T, Container >::raw_data_swap(dimension_type const& size, container_type& data){
    if(elements(size) != data.size()){
        throw error::data_size("mitrax::matrix<>::raw_data_swap", size, data);
    }
    using std::swap;
    swap(data_, data);
}


template < typename T, typename Container >
inline
void
matrix< T, Container >::resize(
    matrix< T, Container >::dimension_type const& size,
    bool preserve,
    const_reference fill
){
    container_type init(elements(size), fill); // Neuer Container

    if(preserve){
        // Länge der pro Zeile zu kopierenden Elemente ermitteln
        size_type min_columns = std::min(size.columns(), dimension_.columns());
        // Differenz von kurzen und langen Zeilen
        size_type dif_columns = std::max(size.columns(), dimension_.columns()) - min_columns;
        // Iteratoren beider Container
        iterator  data_iter = data_.begin();
        iterator  init_iter = init.begin();
        // Iterator mit den längeren Zeilen ermitteln
        iterator& bigger    = size.columns() < dimension_.columns() ? data_iter : init_iter;
        // Zeilenweise durchlaufen
        for(size_type i = size_type(); i < size.rows() && i < dimension_.rows(); ++i){
            // Iteratoren hinter das Ende des zu kopierenden Bereiches setzen
            iterator begin_data_iter = data_iter;
            iterator begin_init_iter = init_iter;
            data_iter += min_columns;
            init_iter += min_columns;
            // Kopie durchführen
            std::copy(begin_data_iter, data_iter, begin_init_iter);
            // Iterator mit den längeren Zeilen auf den Anfang der nächsten Zeile setzen
            bigger += dif_columns;
        }
    }

    using std::swap;
    swap(data_, init);
    dimension_ = size;
}

template < typename T, typename Container >
inline
void
matrix< T, Container >::resize(
    size_type const& rows,
    size_type const& columns,
    bool preserve,
    const_reference fill
){
    resize(dimension_type(rows, columns), preserve, fill);
}

template < typename T, typename Container >
inline
void
matrix< T, Container >::reinit(
    dimension_type const& size,
    container_type& data
){
    raw_data_swap(size, data);
    dimension_ = size;
}

template < typename T, typename Container >
inline
void
matrix< T, Container >::reinit(
    size_type const& rows,
    size_type const& columns,
    container_type& data
){
    reinit(dimension_type(rows, columns), data);
}


template < typename T, typename Container >
inline
void
matrix< T, Container >::erase_row(
    size_type const& number,
    size_type const& count
){
    if(rows() < number + count){
        throw error::row_access("mitrax::matrix<>::erase_row", dimension_, rows());
    }

    // Zeilen löschen
    iterator iter = data_.begin();
    iter += number * columns();
    data_.erase(iter, iter + count * columns());
    // Dimension anpassen
    dimension_.resize(rows() - count, columns());
}

template < typename T, typename Container >
inline
void
matrix< T, Container >::erase_column(
    size_type const& number,
    size_type const& count
){
    if(columns() < number + count){
        throw error::column_access("mitrax::matrix<>::erase_column", dimension_, columns());
    }

    // Zeilen löschen
    for(size_type i = size_type(); i < rows(); ++i){
        iterator iter = data_.begin();
        iter += number + (columns() - count) * i;
        data_.erase(iter, iter + count);
    }
    // Dimension anpassen
    dimension_.resize(rows(), columns() - count);
}



template < typename T, typename Container >
inline
void
matrix< T, Container >::swap(
    matrix< T, Container >& m
){
    using std::swap;
    swap(data_, m.data_);
    swap(dimension_, m.dimension_);
}

template < typename T, typename Container >
inline
void
swap(
    matrix< T, Container >& lhs,
    matrix< T, Container >& rhs
){
    lhs.swap(rhs);
}


}

#endif