#include <cstdlib>
#include <ios>
#include <iostream>
#include <iomanip>
#include <stdexcept>

#include "Header.hh"
#include "StringTools.hh"

#include "ContainerBaseTemplate.hh"
#include "ContainerForVector.hh"
#include "ContainerForMatrix.hh"

Int4 main(Int4 argc, Char *argv[] ) {

    StringTools tools;
    ContainerForMatrix<Int4> int4MatrixMap;

    std::cout << "map.empty(): " << int4MatrixMap.empty() << endl;

    string key0=string("key0");
    string key1=string("longkey1");
    string key2=string("key2");

    vector< vector< Int4 > > val0= vector< vector< Int4 > >(2, vector<Int4>(2, 0));
    vector< vector< Int4 > > val1= vector< vector< Int4 > >(10, vector<Int4>(10, 0));

    int4MatrixMap.add(key0, val0);
    int4MatrixMap.add(key1, val1);

    std::cout << "map.size(): " << int4MatrixMap.size() << endl;

    int4MatrixMap.dump(0);
    int4MatrixMap.dump(2);

    std::cout << "################    get test    ################" << endl;
    std::cout << "#### get the value of an element ####" << endl;
    std::cout << "map.get(key0, 0, 1)=" << int4MatrixMap.get(key0, 0, 1) << endl;
    try { // exception test for an invalid key
        std::cout << "map.get(key2, 0, 1)=" << int4MatrixMap.get(key2, 0, 10) << endl;
    } catch (invalid_argument e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range row index
        std::cout << "map.get(key0, 10, 1)=" << int4MatrixMap.get(key0, 10, 1) << endl;
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range column index
        std::cout << "map.get(key0, 0, 10)=" << int4MatrixMap.get(key0, 0, 10) << endl;
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }
    std::cout << endl;

    std::cout << "#### get the specified row ####" << endl;
    std::cout << "map.getRow(val0, 0)=" << tools.VecInt4ToString(int4MatrixMap.getRow(key0, 0)) << endl;
    try { // exception test for an invalid key
        std::cout << "map.getRow(key2, 0)=" << tools.VecInt4ToString(int4MatrixMap.getRow(key2, 0)) << endl;
    } catch (invalid_argument e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range row index
        std::cout << "map.getRow(key0, 10)=" << tools.VecInt4ToString(int4MatrixMap.getRow(key0, 10)) << endl;
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }
    std::cout << endl;

    std::cout << "#### get the specified row ####" << endl;
    std::cout << "map.getCol(val0, 0)=" << tools.VecInt4ToString(int4MatrixMap.getCol(key0, 0)) << endl;
    try { // exception test for an invalid key
        std::cout << "map.getCol(key2, 0)=" << tools.VecInt4ToString(int4MatrixMap.getCol(key2, 0)) << endl;
    } catch (invalid_argument e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range row index
        std::cout << "map.getCol(key0, 10)=" << tools.VecInt4ToString(int4MatrixMap.getCol(key0, 10)) << endl;
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }
    std::cout << endl;

    std::cout << "################    replace test    ################" << endl;
    std::cout << "#### replace the value of an element ####" << endl;
    std::cout << "map.replace(key0, 0, 0, 1)" << endl;
    int4MatrixMap.replace(key0, 0, 0, 1);
    try { // exception test for an invalid key
        std::cout << "map.replace(key2, 0, 1, 2)" << endl;
        int4MatrixMap.replace(key2, 0, 1, 2);
    } catch (invalid_argument e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range row index
        std::cout << "map.replace(key0, 10, 1, 2)" << endl;
        int4MatrixMap.replace(key0, 10, 1, 2);
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range column index
        std::cout << "map.replace(key0, 0, 10, 2)=" << endl;
        int4MatrixMap.replace(key0, 0, 10, 2);
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }
    int4MatrixMap.dump(0);
    std::cout << endl;

    std::cout << "#### replace the specified row ####" << endl;
    vector< Int4 > v = vector< Int4 >(2, 10);
    std::cout << "v=" << tools.VecInt4ToString(v) << endl;
    std::cout << "map.replaceRow(key0, 0, v)" << endl;
    int4MatrixMap.replaceRow(key0, 0, v);
    try { // exception test for an invalid key
        std::cout << "map.replaceRow(key2, 0, v)" << endl;
        int4MatrixMap.replaceRow(key2, 0, v);
    } catch (invalid_argument e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range row index
        std::cout << "map.replaceRow(key0, 10 v)" << endl;
        int4MatrixMap.replaceRow(key0, 10, v);
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }

    int4MatrixMap.dump(0);
    std::cout << endl;

    std::cout << "#### replace the specified column ####" << endl;
    std::cout << "v=" << tools.VecInt4ToString(v) << endl;
    std::cout << "map.replaceCol(key0, 0, v)" << endl;
    int4MatrixMap.replaceCol(key0, 0, v);
    try { // exception test for an invalid key
        std::cout << "map.replaceCol(key2, 0, v)" << endl;
        int4MatrixMap.replaceCol(key2, 0, v);
    } catch (invalid_argument e) {
        std::cerr << e.what() << endl;
    }

    try { // exception test for an out- of-range column index
        std::cout << "map.replaceCol(key0, 10 v)" << endl;
        int4MatrixMap.replaceCol(key0, 10, v);
    } catch (out_of_range e) {
        std::cerr << e.what() << endl;
    }

    int4MatrixMap.dump(0);
    std::cout << endl;

    exit(EXIT_SUCCESS);
}
