// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2016, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================

#include <iostream>
#include <fstream>
#include <sstream>
#include <typeinfo>
#include <ctime>

#include <map>
#include <set>

#include <seqan/map.h>
#include <seqan/misc/set.h>
#include <seqan/find.h>

using namespace std;
using namespace seqan;

//////////////////////////////////////////////////////////////////////////////

template <typename TMap>
void Test_STLSet()
{
    //typedef typename Value<TMap>::Type TValue;
    typedef typename Iterator<TMap>::Type TIterator;

    TMap map;

    SEQAN_ASSERT(length(map) == 0);

    insert(map, 2);
    SEQAN_ASSERT(hasKey(map,2));
    SEQAN_ASSERT(!hasKey(map,5));

    insert(map, 5);
    SEQAN_ASSERT(hasKey(map,5));

    insert(map, 1);
    SEQAN_ASSERT(hasKey(map, 1));

    int arr1[] = {1, 2, 5};
    TIterator it;
    it = begin(map);
    int i;
    for (i = 0; !atEnd(it); ++i)
    {
        SEQAN_ASSERT(i < 3);
        SEQAN_ASSERT(key(it) == arr1[i]);
        goNext(it);
    }
    SEQAN_ASSERT(i == 3);

    it = find(map, 2);

    TIterator it2(it);
    SEQAN_ASSERT(it == it2);
    SEQAN_ASSERT(key(it) == 2);
    SEQAN_ASSERT(key(it2) == 2);

    goNext(it2);
    SEQAN_ASSERT(it != it2);

    erase(map, it);
    SEQAN_ASSERT(!hasKey(map,2));

    erase(map, 5);
    SEQAN_ASSERT(!hasKey(map,5));

    clear(map);
    SEQAN_ASSERT(length(map) == 0);
}


template <typename TMap>
void Test_NoCargo_Single()
{
    //typedef typename Value<TMap>::Type TValue;
    typedef typename Iterator<TMap>::Type TIterator;

    TMap map;

    SEQAN_ASSERT(length(map) == 0);

    insert(map, 2);
    SEQAN_ASSERT(map[2]);
    SEQAN_ASSERT(!map[5]);

    TMap map2;
    map2 = map;
    SEQAN_ASSERT(map2[2]);
    SEQAN_ASSERT(!map2[5]);

    insert(map, 5);
    SEQAN_ASSERT(map[5]);

    insert(map, 1);
    SEQAN_ASSERT(hasKey(map, 1));

    int arr1[] = {1, 2, 5};
    TIterator it = begin(map);
    int i;
    for (i = 0; !atEnd(it); ++i)
    {
        SEQAN_ASSERT(i < 3);
        SEQAN_ASSERT(key(it) == arr1[i]);
        goNext(it);
    }
    SEQAN_ASSERT(i == 3);

    it = find(map, 2);
    erase(map, it);
    SEQAN_ASSERT(!map[2]);
}

//////////////////////////////////////////////////////////////////////////////

template <typename TMap>
void Test_STLMap()
{
    typedef typename Value<TMap>::Type TValue;
    typedef typename Iterator<TMap>::Type TIterator;

    TMap map;

    SEQAN_ASSERT(length(map) == 0);

    insert(map, TValue(3, 1) );
    insert(map, TValue(1, 2) );
    insert(map, 10);
       map[10] = 3;

    insert(map, 2, 4);

    SEQAN_ASSERT(length(map) == 4);

    int arr1[] = {2, 4, 1, 3};
    int arr2[] = {1, 2, 3, 10};
    TIterator it;
    it = begin(map);
    int i;
    for (i = 0; !atEnd(it); ++i)
    {
        SEQAN_ASSERT(i < 4);
        SEQAN_ASSERT(cargo(it) == arr1[i]);
        SEQAN_ASSERT(key(it) == arr2[i]);
        //SEQAN_ASSERT(key(value(it)) == arr2[i]);
        goNext(it);
    }
    SEQAN_ASSERT(i == 4);

    map[8] = 5;
    map[2] = 6;

    SEQAN_ASSERT(mapValue(map, 8) == 5);

    int arr3[] = {2, 6, 1, 5, 3};
    int arr4[] = {1, 2, 3, 8, 10};
    i = 0;
    for (it = begin(map); it != end(map); ++it)
    {
        SEQAN_ASSERT(i < 5);
        SEQAN_ASSERT(cargo(it) == arr3[i]);
        SEQAN_ASSERT(key(it) == arr4[i]);
        ++i;
    }
    SEQAN_ASSERT(i == 5);

    it = find(map, 7);
    SEQAN_ASSERT(it);
    SEQAN_ASSERT(key(it) == 8);
    SEQAN_ASSERT(cargo(it) == 5);

    TIterator it2(it);
    SEQAN_ASSERT(it2);
    SEQAN_ASSERT(key(it2) == 8);
    SEQAN_ASSERT(cargo(it2) == 5);

    //SEQAN_ASSERT(key(value(it)) == 8);
    //SEQAN_ASSERT(cargo(*it) == 5);
    //cargo(value(it)) = 20;
    //SEQAN_ASSERT(cargo(it) == 20);

    SEQAN_ASSERT(it != begin(map));
    SEQAN_ASSERT(it == it);

    SEQAN_ASSERT(hasKey(map, 8));
    SEQAN_ASSERT(length(map) == 5);
    erase(map, it);
    SEQAN_ASSERT(!hasKey(map, 8));
    SEQAN_ASSERT(length(map) == 4);

    erase(map, 2);
    SEQAN_ASSERT(!hasKey(map, 2));
    SEQAN_ASSERT(length(map) == 3);

    erase(map, 8);
    SEQAN_ASSERT(length(map) == 3);

    clear(map);
    SEQAN_ASSERT(length(map) == 0);
}

//////////////////////////////////////////////////////////////////////////////

template <typename TMap>
void Test_Cargo_Single()
{
    typedef typename Value<TMap>::Type TValue;
    typedef typename Iterator<TMap>::Type TIterator;

    TMap map;

    SEQAN_ASSERT(length(map) == 0);

    insert(map, TValue(3, 1) );
    insert(map, TValue(1, 2) );
    insert(map, 10, 3);
    insert(map, 2, 4);

    SEQAN_ASSERT(length(map) == 4);

    int arr1[] = {2, 4, 1, 3};
    int arr2[] = {1, 2, 3, 10};
    TIterator it = begin(map);
    int i;
    for (i = 0; !atEnd(it); ++i)
    {
        SEQAN_ASSERT(i < 4);
        SEQAN_ASSERT(cargo(it) == arr1[i]);
        SEQAN_ASSERT(key(it) == arr2[i]);
        SEQAN_ASSERT(key(value(it)) == arr2[i]);
        goNext(it);
    }
    SEQAN_ASSERT(i == 4);

    map[8] = 5;
    map[2] = 6;

    SEQAN_ASSERT(mapValue(map, 8) == 5);

    int arr3[] = {2, 6, 1, 5, 3};
    int arr4[] = {1, 2, 3, 8, 10};
    i = 0;
    for (it = begin(map); it != end(map); ++it)
    {
        SEQAN_ASSERT(i < 5);
        SEQAN_ASSERT(cargo(it) == arr3[i]);
        SEQAN_ASSERT(key(it) == arr4[i]);
        ++i;
    }
    SEQAN_ASSERT(i == 5);

    it = find(map, 7);
    SEQAN_ASSERT(it != 0);
    SEQAN_ASSERT(key(it) == 8);
    SEQAN_ASSERT(cargo(it) == 5);

    SEQAN_ASSERT(key(value(it)) == 8);
    SEQAN_ASSERT(cargo(*it) == 5);
    cargo(value(it)) = 20;
    SEQAN_ASSERT(cargo(it) == 20);

    SEQAN_ASSERT(it != begin(map));
    SEQAN_ASSERT(it == it);

    SEQAN_ASSERT(hasKey(map, 8));
    SEQAN_ASSERT(length(map) == 5);
    erase(map, it);
    SEQAN_ASSERT(!hasKey(map, 8));
    SEQAN_ASSERT(length(map) == 4);

    erase(map, 2);
    SEQAN_ASSERT(!hasKey(map, 2));
    SEQAN_ASSERT(length(map) == 3);

    erase(map, 8);
    SEQAN_ASSERT(length(map) == 3);

    clear(map);
    SEQAN_ASSERT(length(map) == 0);
}

//////////////////////////////////////////////////////////////////////////////

template <typename TMap>
void Test_Cargo_Multiple()
{
    //typedef typename Value<TMap>::Type TValue;
    //typedef typename Iterator<TMap>::Type TIterator;

    TMap map;

    insert(map, 2, 4);
    SEQAN_ASSERT(length(map) == 1);

    insert(map, 2, 5);
    SEQAN_ASSERT(length(map) == 1);

    add(map, 2, 6);
    SEQAN_ASSERT(length(map) == 2);

    add(map, 3, 7);
    SEQAN_ASSERT(length(map) == 3);

    eraseAll(map, 2);
    SEQAN_ASSERT(length(map) == 1);
}

//////////////////////////////////////////////////////////////////////////////

void Test_Skiplist_Extra()
{
    typedef Pair<char, int> TValue;
    typedef Map<TValue, Skiplist< > > TMap;
    TMap map;

    map[8] = 5;
    SEQAN_ASSERT(value(map, 8) == TValue(8, 5) );

    map[2] = 3;
    TMap map2 = map;
    SEQAN_ASSERT(length(map2) == 2);
    SEQAN_ASSERT(map2[8] == 5);
    SEQAN_ASSERT(map2[2] == 3);
}

//////////////////////////////////////////////////////////////////////////////

template <typename TMap1, typename TMap2>
void CompareMaps_(TMap1 & map1, TMap2 & map2)
{
    typedef typename Iterator<TMap1>::Type TIter1;
    typedef typename Iterator<TMap2>::Type TIter2;

    SEQAN_ASSERT(length(map1) == length(map2));

    TIter1 it1(map1);
    TIter2 it2(map2);

    while (!atEnd(it1))
    {
        SEQAN_ASSERT(!atEnd(it2));
        SEQAN_ASSERT(key(it1) == key(it2));
        SEQAN_ASSERT(cargo(it1) == cargo(it2));

        goNext(it1);
        goNext(it2);
    }

    SEQAN_ASSERT(atEnd(it2));
}

//____________________________________________________________________________

void Test_Skiplist_Stress()
{
    typedef Pair<int, int> TValue;
    typedef Map<TValue, Skiplist< > > TSkipList;
    typedef Iterator<TSkipList>::Type TSkipListIterator;

    typedef std::map<int, int> TSTDMap;
    typedef Iterator<TSTDMap>::Type TSTDMapIterator;

    TSkipList sl;
    TSkipList sl2;
    TSTDMap stdmap;

    TSkipListIterator sl_it(sl);
    TSTDMapIterator stdmap_it(stdmap);

    for (int j = 0; j < 100; ++j)
    {
        cout << ".";

        for (int i = 0; i < 500; ++i)
        {
            int _key = rand();
            int value = rand();

            insert(sl, _key, value);
            insert(stdmap, _key, value);
        }

        CompareMaps_(sl, stdmap);

        for (int i = 0; i < 100; ++i)
        {
            int _key = (rand() + (rand() << 16));

            sl_it = find(sl, _key);
            stdmap_it = find(stdmap, _key);

            SEQAN_ASSERT(atEnd(sl_it) == atEnd(stdmap_it));
            if (! atEnd(sl_it))
            {
                SEQAN_ASSERT(key(sl_it) == key(stdmap_it));
                SEQAN_ASSERT(cargo(sl_it) == cargo(stdmap_it));

                erase(sl, key(sl_it));
                erase(stdmap, key(stdmap_it));
            }
        }

        CompareMaps_(sl, stdmap);

        sl2 = sl;
        CompareMaps_(sl2, stdmap);
    }
    cout << "\n";

}

//////////////////////////////////////////////////////////////////////////////

void Main_Test_Map()
{
    Test_Cargo_Single< Map< Pair<char, int>, Skiplist< > > >();
    Test_Cargo_Single< Map< Pair<char, int>, VectorSet< > > >();

    Test_STLMap< std::map<char, int> >();

    Test_NoCargo_Single< Map< char, Skiplist< > > >();
    Test_NoCargo_Single< Map< char, VectorSet< > > >();

    Test_STLSet< std::set<char> >();

       Test_Cargo_Multiple< Map< Pair<char, int>, Skiplist< > > >();

    Test_Skiplist_Extra();

    Test_Skiplist_Stress();
}


SEQAN_DEFINE_TEST(test_map_map)
{
    Main_Test_Map();
}

