/*
 * PartitionGTest.cpp
 *
 *  Created on: 04.12.2013
 *      Author: Maximilian Vogel (uocvf@student.kit.edu)
 */

#include <iostream>

#include <gtest/gtest.h>

#include <networkit/structures/Partition.hpp>

namespace NetworKit {

class PartitionGTest : public testing::Test {};

TEST_F(PartitionGTest, testConstructor) {
    Partition p(10);
    EXPECT_EQ(0u, p.lowerBound());
    EXPECT_EQ(1u, p.upperBound());
}

TEST_F(PartitionGTest, testConstructorIndexAndDefaultValue) {
    Partition p(10, 1);
    EXPECT_EQ(0u, p.lowerBound());
    EXPECT_EQ(1u, p.upperBound());
}

TEST_F(PartitionGTest, testVectorConstructor) {
    Partition p(10);
    Partition p2(p.getVector());
    EXPECT_EQ(0u, p2.lowerBound());
    EXPECT_EQ(1u, p2.upperBound());
}

TEST_F(PartitionGTest, testAllToSingletons) {
    Partition p(10);
    p.allToSingletons();
    for (int i = 0; i < 10; i++) {
        EXPECT_TRUE(p[i] != p[(i + 1) % 10]);
    }
}

TEST_F(PartitionGTest, testAddToSubsetSuccess) {
    // Add to Subset really necessary?
    Partition p(10);
    p.addToSubset(0, 8);
    p.addToSubset(0, 3);
    EXPECT_EQ(p[8], p[3]);
}

TEST_F(PartitionGTest, testMoveToSubsetSuccess) {
    Partition p(10);
    p.allToSingletons();
    p.moveToSubset(p[3], 8);
    EXPECT_EQ(p[8], p[3]);
}

TEST_F(PartitionGTest, testMergeSubsets) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    EXPECT_EQ(p[9], p[7]);
}

/*TEST_F(PartitionGTest, testIsOnePartitionFalse) {
    Partition p(10);
    p.allToSingletons();
    EXPECT_FALSE(p.isOnePartition({}));
}

TEST_F(PartitionGTest, testIsOnePartitionFalse2) {
    Partition p(10);
    p.allToSingletons();
    p.forEntries([&](index e,index s){
        p.moveToSubset(p[0],e);
    });
    p.toSingleton(9);
    EXPECT_FALSE(p.isOnePartition({}));
}

TEST_F(PartitionGTest, testIsOnePartitionTrue) {
    Partition p(10);
    p.allToSingletons();
    p.forEntries([&](index e,index s){
        p.moveToSubset(p[9],e);
    });
    EXPECT_TRUE(p.isOnePartition({}));
}

TEST_F(PartitionGTest, testIsSingletonPartitionTrue) {
    Partition p(10);
    p.allToSingletons();
    EXPECT_TRUE(p.isSingletonPartition({}));
}

TEST_F(PartitionGTest, testIsSingletonPartitionFalse) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0],p[9]);
    EXPECT_FALSE(p.isSingletonPartition({}));
}*/

TEST_F(PartitionGTest, testNumberOfSubsets) {
    count n = 6542;
    Partition p(n);
    p.allToSingletons();
    EXPECT_EQ(p.numberOfSubsets(), n);
}

TEST_F(PartitionGTest, testNumberOfSubsets2) {
    count n = 6542;
    Partition p(n);
    p.allToSingletons();
    p.parallelForEntries([&](index e, index) { p.moveToSubset(p[0], e); });
    EXPECT_EQ(1u, p.numberOfSubsets());
}

TEST_F(PartitionGTest, testNumberOfSubsets3) {
    count n = 6542;
    count subsets = n / 2;
    Partition p(n);
    p.allToSingletons();
    for (uint64_t i = 0; i < n; i += 2) {
        p.mergeSubsets(p[i], p[i + 1]);
    }
    EXPECT_EQ(p.numberOfSubsets(), subsets);
}
TEST_F(PartitionGTest, testLowerBound) {
    count n = 6542;
    Partition p(n);
    p.allToSingletons();
    EXPECT_EQ(0u, p.lowerBound());
}

TEST_F(PartitionGTest, testUpperBound) {
    count n = 6542;
    Partition p(n);
    p.allToSingletons();
    // ID start with 0
    count ub = n;
    EXPECT_EQ(ub, p.upperBound());
}

TEST_F(PartitionGTest, testUpperBound2) {
    count n = 6542;
    Partition p(n);
    p.allToSingletons();
    for (int i = 0; i < 6542; i += 2) {
        p.mergeSubsets(p[i], p[i + 1]);
    }
    // 6542 because of singletons
    // +(6542/2) = 3271 merge operations
    // = 9813
    EXPECT_EQ(9813u, p.upperBound());
}

TEST_F(PartitionGTest, testContainsSuccessSingletons) {
    Partition p(10);
    p.allToSingletons();
    EXPECT_TRUE(p.contains(8));
}

TEST_F(PartitionGTest, testContainsSuccessAfterMerges) {
    Partition p(10);
    p.allToSingletons();
    p.forEntries([&](index e, index) {
        index n = (e == 9) ? 0 : e + 1;
        p.mergeSubsets(p[e], p[n]);
    });
    p.toSingleton(9);
    EXPECT_TRUE(p.contains(8));
}

TEST_F(PartitionGTest, testContainsNodeOutOfRange) {
    Partition p(10);
    p.allToSingletons();
    EXPECT_FALSE(p.contains(15));
}

TEST_F(PartitionGTest, testContainsNodeWithoutSubset) {
    Partition p(10);
    for (int i = 0; i < 10; i++) {
        EXPECT_FALSE(p.contains(i));
    }
}

TEST_F(PartitionGTest, testInSameSubsetTrue) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    EXPECT_TRUE(p.inSameSubset(9, 7));
}

TEST_F(PartitionGTest, testInSameSubsetFalse) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    EXPECT_FALSE(p.inSameSubset(9, 4));
}

TEST_F(PartitionGTest, testCompact) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    p.compact();
    EXPECT_EQ(5u, p.upperBound()); // This is only a weak test

    // the following is a deeper test that checks if partition ids and structures match
    EXPECT_EQ(p[0], p[1]);
    EXPECT_EQ(p[0], p[2]);
    EXPECT_NE(p[0], p[3]);
    EXPECT_NE(p[0], p[4]);
    EXPECT_NE(p[0], p[5]);
    EXPECT_NE(p[0], p[6]);
    EXPECT_NE(p[3], p[4]);
    EXPECT_NE(p[3], p[5]);
    EXPECT_NE(p[3], p[6]);
    EXPECT_NE(p[4], p[5]);
    EXPECT_NE(p[4], p[6]);
    EXPECT_NE(p[5], p[6]);
    EXPECT_NE(p[6], p[7]);
    EXPECT_EQ(p[0], p[7]);
    EXPECT_EQ(p[0], p[8]);
    EXPECT_EQ(p[0], p[9]);
}

TEST_F(PartitionGTest, testSubsetSizes) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    std::vector<count> sizesFunction = p.subsetSizes();
    std::vector<count> sizesControl = {1, 1, 1, 1, 6};
    for (index i = 0; i < sizesFunction.size(); i++) {
        EXPECT_EQ(sizesFunction[i], sizesControl[i]);
    }
}

TEST_F(PartitionGTest, testSubsetSizeMap) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    auto subsizemap = p.subsetSizeMap();
    EXPECT_EQ(6u, subsizemap[p[0]]);
    EXPECT_EQ(1u, subsizemap[p[3]]);
    EXPECT_EQ(1u, subsizemap[p[4]]);
    EXPECT_EQ(1u, subsizemap[p[5]]);
    EXPECT_EQ(1u, subsizemap[p[6]]);
}

TEST_F(PartitionGTest, testGetMembers) {
    Partition p(10);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    auto members = p.getMembers(p[0]);
    std::vector<index> membersControl = {0, 1, 2, 7, 8, 9};
    index i = 0;
    for (auto it = members.begin(); it != members.end(); it++) {
        EXPECT_EQ(*it, membersControl[i]);
        i++;
    }
}

TEST_F(PartitionGTest, testNumberOfElements) {
    index n = 10;
    Partition p(n);
    p.allToSingletons();
    p.mergeSubsets(p[0], p[9]);
    p.mergeSubsets(p[1], p[8]);
    p.mergeSubsets(p[2], p[7]);
    p.mergeSubsets(p[0], p[1]);
    p.mergeSubsets(p[1], p[2]);
    EXPECT_EQ(n, p.numberOfElements());
}

} /* namespace NetworKit */
