#ifndef HEADER_H_INCLUDED

#define HEADER_H_INCLUDED



#include <string.h>

#include <stdio.h>

#include <stdlib.h>

typedef struct hashentry__s

{

    char songtitle[256], interpreter[256];

} hashentry_t;

typedef struct hasharray__s

{

    hashentry_t *entries;

    long num_entries;

} hasharray_t;

typedef struct hashcontainer__s

{

    hasharray_t *harrays;

    long hashsize;

} hashcontainer_t;



long hash_key(char songtitle[], char interpreter[], long hash_max);

hashcontainer_t *create_hash(long hashsize);

void delete_hash(hashcontainer_t *hashcontainer);

void insert_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[]);

hashentry_t *search_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[]);

void delete_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[]);

void print_hash(hashcontainer_t *hashcontainer);

long count_entries(hashcontainer_t *hashcontainer);

long count_entries_with_songtitle(hashcontainer_t *hashcontainer, char songtitle[]);

void insert_entry_ext_exp(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[], long(*hashkey_gen)(hashentry_t *entry, long hash_max));

void insert_entry_ext(hashcontainer_t *hashcontainer, hashentry_t *hashentry, long(*hashkey_gen)(hashentry_t *entry, long hash_max));

long hashkey_gen1(hashentry_t *entry, long hash_max);

long hashkey_gen2(hashentry_t *entry, long hash_max);

void rehash1(hashcontainer_t *hashcontainer, long(*hashkey_gen)(hashentry_t *entry, long hash_max), long hash_max);

void rehash2(hashcontainer_t *hashcontainer, long(*hashkey_gen)(hashentry_t *entry, long hash_max), long hash_max);



#endif // HEADER_H_INCLUDED



#include "header.h"



long hash_key(char songtitle[], char interpreter[], long hash_max)

{

    unsigned long i, index = 0;

    for (i = 0; i < strlen(songtitle); ++i)

        index = 64 * index + (long)(songtitle[i]);

    for (i = 0; i < strlen(interpreter); ++i)

        index = 64 * index + (long)(interpreter[i]);

    return index % hash_max;

}





long hashkey_gen1(hashentry_t *entry, long hash_max)

{

    unsigned long i, index = 0;

    for (i = 0; i < strlen(entry->songtitle); ++i)

        index = 64 * index + (long)(entry->songtitle[i]);

    for (i = 0; i < strlen(entry->interpreter); ++i)

        index = 64 * index + (long)(entry->interpreter[i]);

    return index % hash_max;

}





hashcontainer_t *create_hash(long hashsize)

{

    unsigned long i;

    hashcontainer_t *new = 0;

    if (!(new = malloc(sizeof(hashcontainer_t))))

    {

        fprintf(stderr, "Memory allocation error in create_hash()");

        exit(-1);

    }

    if (!(new->harrays = calloc(hashsize, sizeof(hasharray_t))))

    {

        fprintf(stderr, "Memory allocation error in create_hash()");

        exit(-1);

    }

    new->hashsize = hashsize;

    for (i = 0; i < hashsize; i++)

    {

        new->harrays[i];

    }

    return new;

}





void delete_hash(hashcontainer_t *hashcontainer)

{

    unsigned long i;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        if (hashcontainer->harrays[i].entries)

        {

            free(hashcontainer->harrays[i].entries);

            hashcontainer->harrays[i].entries = 0;

            hashcontainer->harrays[i].num_entries = 0;

        }

    }

    free(hashcontainer->harrays);

    hashcontainer->harrays = 0;

    hashcontainer->hashsize = 0;

}





void insert_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[])

{

    unsigned long index = hash_key(songtitle, interpreter, hashcontainer->hashsize);

    hashcontainer->harrays[index].num_entries++;

    hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

    strcpy(hashcontainer->harrays[index].entries[hashcontainer->harrays[index].num_entries - 1].songtitle, songtitle);

    strcpy(hashcontainer->harrays[index].entries[hashcontainer->harrays[index].num_entries - 1].interpreter, interpreter);

}





hashentry_t *search_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[])

{

    unsigned long index = hash_key(songtitle, interpreter, hashcontainer->hashsize);

    unsigned long len = hashcontainer->harrays[index].num_entries, i;

    for (i = 0; i < len; i++)

    {

        if ((strcmp(hashcontainer->harrays[index].entries[i].interpreter, interpreter) == 0) && (strcmp(hashcontainer->harrays[index].entries[i].songtitle, songtitle) == 0))

            return (hashcontainer->harrays[index].entries)+i;

    }

    return 0;

}





void delete_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[])

{

    unsigned long index = hash_key(songtitle, interpreter, hashcontainer->hashsize);

    unsigned long len = hashcontainer->harrays[index].num_entries, i;

    for (i = 0; i < len; i++)

    {

        if ((strcmp(hashcontainer->harrays[index].entries[i].interpreter, interpreter) == 0) && (strcmp(hashcontainer->harrays[index].entries[i].songtitle, songtitle) == 0))

        {

            if (i == 0 && len == 1)

            {

                free(hashcontainer->harrays[index].entries);

                hashcontainer->harrays[index].entries = 0;

                hashcontainer->harrays[index].num_entries = 0;

            }

            else

            {

                if (i != len - 1)

                    memcpy(hashcontainer->harrays[index].entries + i, hashcontainer->harrays[index].entries + i + 1, (len - i - 1)* sizeof(hashentry_t));

                hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, --hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

            }

        }

    }

}





void print_hash(hashcontainer_t *hashcontainer)

{

    unsigned long i, j;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        fprintf(stdout, "Hash index %ld\n", i);

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            fprintf(stdout, "%s\t%s\n", hashcontainer->harrays[i].entries[j].songtitle, hashcontainer->harrays[i].entries[j].interpreter);

        }

        fprintf(stdout, "\n");

    }

}





long count_entries(hashcontainer_t *hashcontainer)

{

    unsigned long i, j, count = 0;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

                count++;

        }

    }

    return count;

}





long count_entries_with_songtitle(hashcontainer_t *hashcontainer, char songtitle[])

{

    unsigned long i, j, count = 0;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            if (strcmp(hashcontainer->harrays[i].entries[j].songtitle, songtitle) == 0)

                count++;

        }

    }

    return count;

}





void insert_entry_ext_exp(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[], long(*hashkey_gen)(hashentry_t *entry, long hash_max))

{

    hashentry_t *new = malloc(sizeof(hashentry_t));

    strcpy(new->songtitle, songtitle);

    strcpy(new->interpreter, interpreter);

    unsigned long index = hashkey_gen(new, hashcontainer->hashsize);



    hashcontainer->harrays[index].num_entries++;

    hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

    memcpy(hashcontainer->harrays[index].entries + hashcontainer->harrays[index].num_entries - 1, new, sizeof(hashentry_t));

}





void insert_entry_ext(hashcontainer_t *hashcontainer, hashentry_t *new, long(*hashkey_gen)(hashentry_t *entry, long hash_max))

{

    unsigned long index = hashkey_gen(new, hashcontainer->hashsize);



    hashcontainer->harrays[index].num_entries++;

    hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

    memcpy(hashcontainer->harrays[index].entries + hashcontainer->harrays[index].num_entries - 1, new, sizeof(hashentry_t));

}





hashentry_t *search_entry_ext(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[], long(*hashkey_gen)(hashentry_t *entry, long hash_max))

{

    hashentry_t *new = malloc(sizeof(hashentry_t));

    strcpy(new->songtitle, songtitle);

    strcpy(new->interpreter, interpreter);

    unsigned long index = hashkey_gen(new, hashcontainer->hashsize);



    unsigned long i;

    for (i = 0; i < hashcontainer->harrays[index].num_entries; i++)

    {

        if ((strcmp(hashcontainer->harrays[index].entries[i].interpreter, interpreter) == 0) && (strcmp(hashcontainer->harrays[index].entries[i].songtitle, songtitle) == 0))

            return (hashcontainer->harrays[index].entries)+i;

    }

    return 0;

}





void rehash1(hashcontainer_t *hashcontainer, long(*hashkey_gen)(hashentry_t *entry, long hash_max), long hash_max)

{

    hashcontainer_t *new = create_hash(hash_max);

    unsigned long i, j;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            insert_entry_ext(new, hashcontainer->harrays[i].entries + j, hashkey_gen);

        }

    }

    delete_hash(hashcontainer);

    hashcontainer->harrays = new->harrays;

    hashcontainer->hashsize = new->hashsize;

}





void rehash2(hashcontainer_t *hashcontainer, long(*hashkey_gen)(hashentry_t *entry, long hash_max), long hash_max)

{

    unsigned long i, j, count = 0, len = count_entries(hashcontainer);

    hashentry_t temp[len];

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            strcpy(temp[count].songtitle, hashcontainer->harrays[i].entries[j].songtitle);

            strcpy(temp[count].interpreter, hashcontainer->harrays[i].entries[j].interpreter);

            count++;

        }

    }

    delete_hash(hashcontainer);

    hashcontainer_t *new = create_hash(hash_max);

    for (i = 0; i < len; i++)

    {

        insert_entry_ext(new, &temp[i], hashkey_gen);

    }

    hashcontainer->harrays = new->harrays;

    hashcontainer->hashsize = new->hashsize;

    free(new);

}



#include "source.c"





int main()

{

    hashcontainer_t *container = create_hash(3);



    insert_entry_ext_exp(container, "Title1", "Inter1", hashkey_gen1);

    insert_entry_ext_exp(container, "Title2", "Inter2", hashkey_gen1);

    insert_entry_ext_exp(container, "Title2", "Inter2", hashkey_gen1);

    insert_entry_ext_exp(container, "Title3", "Inter3", hashkey_gen1);

    insert_entry_ext_exp(container, "Title3", "Inter3", hashkey_gen1);

    insert_entry_ext_exp(container, "Title3", "Inter3", hashkey_gen1);

    insert_entry_ext_exp(container, "Title3", "Inter3", hashkey_gen1);

    insert_entry_ext_exp(container, "Title4", "Inter4", hashkey_gen1);

    insert_entry_ext_exp(container, "Title7", "Inter7", hashkey_gen1);

    insert_entry_ext_exp(container, "Title7", "Inter7", hashkey_gen1);

    delete_entry(container, "Title7", "Inter7");



    print_hash(container);

    fprintf(stdout, "\nRehashing...\n\n");

    rehash1(container, hashkey_gen1, 11);

    print_hash(container);



    hashentry_t *temp = search_entry_ext(container, "Title7", "Inter7", hashkey_gen1);

    if (temp)

        fprintf(stdout, "%s\t%s\n", temp->songtitle, temp->interpreter);

    else

        fprintf(stdout, "Not found.\n");



    fprintf(stdout, "%ld entries found.", count_entries(container));



    delete_hash(container);

    free(container);

    return 0;

}
