/**
* Copyright (c) NVIDIA CORPORATION & AFFILIATES, 2001-2014. ALL RIGHTS RESERVED.
* Copyright (C) UT-Battelle, LLC. 2014. ALL RIGHTS RESERVED.
* See file LICENSE for terms.
*/

#include <common/test.h>
extern "C" {
#include <ucs/algorithm/crc.h>
#include <ucs/algorithm/qsort_r.h>
#include <ucs/algorithm/string_distance.h>
}
#include <vector>

class test_algorithm : public ucs::test {
protected:

    static int compare_func(const void *elem1, const void *elem2)
    {
        return *(const int*)elem1 - *(const int*)elem2;
    }

    static int compare_func_r(const void *elem1, const void *elem2, void *arg)
    {
        EXPECT_TRUE(MAGIC == arg);
        return compare_func(elem1, elem2);
    }

    static void *MAGIC;
};

void *test_algorithm::MAGIC = (void*)0xdeadbeef1ee7a880ull;

UCS_TEST_F(test_algorithm, qsort_r) {
    for (int i = 0; i < 1000 / ucs::test_time_multiplier(); ++i) {
        unsigned nmemb = ucs::rand() % 100;

        std::vector<int> vec;
        for (unsigned j = 0; j < nmemb; ++j) {
            vec.push_back(ucs::rand() % 200);
        }

        std::vector<int> vec2 = vec;
        qsort(&vec2[0], nmemb, sizeof(int), compare_func);

        ucs_qsort_r(&vec[0], nmemb, sizeof(int), compare_func_r, MAGIC);
        ASSERT_EQ(vec2, vec);
    }
}

UCS_TEST_F(test_algorithm, crc16) {
    std::string test_str;

    test_str = "";
    EXPECT_EQ(0u, ucs_crc16_string(test_str.c_str()));

    test_str = "0";
    EXPECT_EQ(0xc1fbu, ucs_crc16_string(test_str.c_str()));

    test_str = "01";
    EXPECT_EQ(0x99efu, ucs_crc16_string(test_str.c_str()));

    test_str = "012";
    EXPECT_EQ(0xfd89u, ucs_crc16_string(test_str.c_str()));

    test_str = "0123";
    EXPECT_EQ(0xea54u, ucs_crc16_string(test_str.c_str()));

    test_str = "01234";
    EXPECT_EQ(0x9394u, ucs_crc16_string(test_str.c_str()));

    test_str = "012345";
    EXPECT_EQ(0x4468u, ucs_crc16_string(test_str.c_str()));

    test_str = "0123456";
    EXPECT_EQ(0x4bc7u, ucs_crc16_string(test_str.c_str()));

    test_str = "01234567";
    EXPECT_EQ(0x07bcu, ucs_crc16_string(test_str.c_str()));

    test_str = "012345678";
    EXPECT_EQ(0x3253u, ucs_crc16_string(test_str.c_str()));

    test_str = "0123456789";
    EXPECT_EQ(0x3c16u, ucs_crc16_string(test_str.c_str()));
}

UCS_TEST_F(test_algorithm, crc32) {
    std::string test_str;

    test_str = "";
    EXPECT_EQ(0u, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "0";
    EXPECT_EQ(0xf4dbdf21ul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "01";
    EXPECT_EQ(0xcf412436ul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "012";
    EXPECT_EQ(0xd5a06ab0ul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "0123";
    EXPECT_EQ(0xa6669d7dul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "01234";
    EXPECT_EQ(0xdda47024ul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "012345";
    EXPECT_EQ(0xb86f6b0ful, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "0123456";
    EXPECT_EQ(0x8dbf08eeul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "01234567";
    EXPECT_EQ(0x2d803af5ul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "012345678";
    EXPECT_EQ(0x37fad1baul, ucs_crc32(0, test_str.c_str(), test_str.size()));

    test_str = "0123456789";
    EXPECT_EQ(0xa684c7c6ul, ucs_crc32(0, test_str.c_str(), test_str.size()));
}

UCS_TEST_F(test_algorithm, string_distance) {
    // Empty strings
    EXPECT_EQ(0u, ucs_string_distance("", ""));

    // First string empty
    EXPECT_EQ(8u, ucs_string_distance("", "aabbccdd"));

    // Second string empty
    EXPECT_EQ(8u, ucs_string_distance("aabbccdd", ""));

    // 2 identical strings
    EXPECT_EQ(0u, ucs_string_distance("aabbccdd", "aabbccdd"));

    // No common chars
    EXPECT_EQ(8u, ucs_string_distance("aabbccdd", "eeffgghh"));

    // Replace
    EXPECT_EQ(2u, ucs_string_distance("aabbccdd", "aabeccfd"));

    // Swap
    EXPECT_EQ(2u, ucs_string_distance("aabbccdd", "aabcbcdd"));

    // Subtract
    EXPECT_EQ(2u, ucs_string_distance("aabbccdd", "aabcdd"));

    // Add
    EXPECT_EQ(2u, ucs_string_distance("aabbccdd", "aabbeeccdd"));

    // Combinations
    EXPECT_EQ(4u, ucs_string_distance("aabbccddee", "afbccdede"));
    EXPECT_EQ(4u, ucs_string_distance("aabbccddeeff", "ababccdgedeff"));
    EXPECT_EQ(6u, ucs_string_distance("aabbccddeeff", "aagbbhddefefii"));
}
