/*
 * Copyright ©2026 Soham Pardeshi. All rights reserved.
 * Permission is hereby granted to students registered for University of
 * Washington CSE 333 for use solely during Summer Quarter 2026 for
 * purposes of the course. No other use, copying, distribution, or
 * modification is permitted without prior written consent. Copyrights
 * for third-party components of this work must be honored. Instructors
 * interested in reusing these course materials should contact the author.
 */

#ifndef HW1_HASHTABLE_H_
#define HW1_HASHTABLE_H_

#include <stdbool.h>    // for bool type (true, false)
#include <stdint.h>     // for uint64_t, etc.

///////////////////////////////////////////////////////////////////////////////
// A HashTable is a automatically-resizing chained hash table.
//
// We provide the interface; your job is to provide the implementation.
//
// This hash table allows the caller to pass in an arbitrary value type
// (via the void*).  However, we require that the caller hash the key before
// providing it to the table for storage.  It's up to the customer to
// figure out how to produce an appropriate hash key, but below we provide
// an implementation of FNV hashing to help them out.
//
// As the load factor approaches 1, linked lists hanging off of each bucket
// will start to grow.  This implementation will dynamically resize the
// hashtable when the load factor exceeds 3.  It will multiple the number
// of buckets in the hashtable by 9, so that post-resize load factor is 1/3.
//
// To hide the implementation of HashTable, we declare the "struct ht"
// structure and its associated typedef here, but we *define* the structure
// in the internal header HashTable_priv.h.  This lets us define a pointer
// to HashTable as a new type, while leaving the implementation details
// opaque to the customer.
typedef struct ht HashTable;

// Key and value type definitions:
// For generality, a value must be large enough to hold a pointer.
// If the client's data is no bigger than a pointer, a copy of that
// data can be stored in the HashTable, by casting it to the HTValue_t
// type.  Otherwise, a pointer to the client's data is maintained in
// the table.
typedef uint64_t HTKey_t;      // hash table key type
typedef void*    HTValue_t;    // hash table value type
typedef struct {
  HTKey_t   key;      // the key in the (key,value) pair
  HTValue_t value;    // the value in the (key,value) pair
} HTKeyValue_t;

// When freeing a HashTable, customers need to pass a pointer to a function
// that frees the payload.  The pointed-to function is invoked once for each
// value in the HashTable.
//
// Additional Note: This is a function pointer. Please refer to the end of
// Lecture 3 slides (Pointers, pointers, pointers...) for more detail on the
// syntax and usage.
typedef void(*ValueFreeFnPtr)(HTValue_t value);

// FNV hash implementation.
//
// Customers can use this to hash an arbitrary sequence of bytes into
// a 64-bit key suitable for using as a hash key.  If you're curious, you
// can read about FNV hashing here:
//     http://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
//
// Arguments:
// - buffer: a pointer to a len-size buffer of unsigned chars.
// - len: how many bytes are in the buffer.
//
// Returns:
// - a nicely distributed 64-bit hash value suitable for
//   use in a HTKeyValue_t.
HTKey_t FNVHash64(unsigned char *buffer, int len);


// Allocate and return a new HashTable.
//
// Arguments:
// - num_buckets: the number of buckets the hash table should
//   initially contain; MUST be greater than zero.
//
// Returns a pointer to the newly allocated HashTable.
HashTable* HashTable_Allocate(int num_buckets);

// Free a HashTable and its entries.
//
// Arguments:
// - table:  the HashTable to free.  It is unsafe to use table
//   after this function returns.
//
// - value_free_function:  this argument is a pointer to a value
//   freeing function; see above for details.
void HashTable_Free(HashTable *table, ValueFreeFnPtr value_free_function);

// Figure out the number of elements in the hash table.
//
// Arguments:
//
// - table:  the table to query
//
// Returns:
//
// - table size (>=0)
int HashTable_NumElements(HashTable *table);

// Inserts a (key,value) pair into the HashTable.
//
// Arguments:
// - table: the HashTable to insert into.
// - newkeyvalue: the HTKeyValue_t to insert into the table.
// - oldkeyval: if the key in newkeyvalue is already present
//   in the HashTable, that old (key,value) is replaced with
//   newkeyvalue.  In that case, the old (key,value) is returned via
//   this return parameter to the caller.  It's up to the caller
//   to free any allocated memory associated with oldkeyvalue->value.
//
// Returns:
//  - false: if the newkeyvalue was inserted and there was no
//    existing (key,value) with that key.
//  - true: if the newkeyvalue was inserted and an old (key,value)
//    with the same key was replaced and returned through
//    the oldkeyval return parameter.  In this case, the caller assumes
//    ownership of oldkeyvalue.
bool HashTable_Insert(HashTable *table,
                      HTKeyValue_t newkeyvalue,
                      HTKeyValue_t *oldkeyvalue);

// Looks up a key in the HashTable, and if it is present, returns the
// (key,value) associated with it.
//
// Arguments:
// - table: the HashTable to look in.
// - key: the key to look up.
// - keyvalue: if the key is present, a copy of the (key,value) is
//   returned to the caller via this return parameter.  Note that the
//   (key,value) is left in the HashTable, so it is not safe for the
//   caller to free keyvalue->value.
//
// Returns:
//  - false: if the key wasn't found in the HashTable.
//  - true: if the key was found, and therefore the associated (key,value)
//    was returned to the caller via that keyvalue return parameter.
bool HashTable_Find(HashTable *table,
                    HTKey_t key,
                    HTKeyValue_t *keyvalue);

// Removes a (key,value) from the HashTable and returns it to the
// caller.
//
// Arguments:
// - table: the HashTable to look in.
// - key: the key to look up.
// - keyvalue: if the key is present, a copy of (key,value) is returned
//   to the caller via this return parameter and the (key,value) is
//   removed from the HashTable.  Note that the caller is responsible
//   for managing the memory associated with keyvalue->value from
//   this point on.
//
// Returns:
//  - false: if the key wasn't found in the HashTable.
//  - true: if the key was found, and therefore (a) the associated
//    (key,value) was returned to the caller via that keyvalue return
//    parameter, and (b) that (key,value) was removed from the
//    HashTable.
bool HashTable_Remove(HashTable *table,
                      HTKey_t key,
                      HTKeyValue_t *keyvalue);


///////////////////////////////////////////////////////////////////////////////
// HashTable iterator
//
// HashTables support the notion of an iterator, similar to Java iterators.
// You use an iterator to iterate forward through the HashTable.   The order
// in which the iterator goes through the HashTable is undefined, and not
// necessarily deterministic; all that is promised is that each (key,value)
// is visited exactly once.  Also, if the customer uses a HashTable function
// to mutate the hash table, any existing iterators become undefined (ie,
// dangerous to use; arbitrary memory corruption can occur).
typedef struct ht_it HTIterator;  // same trick to hide implementation.

// Manufacture an iterator for the table.  If there are
// elements in the hash table, the iterator is initialized
// to point at the "first" one.  The caller is responsible
// for eventually calling HTIterator_Free.
//
// Arguments:
// - table:  the table from which to return an iterator.
//
// Returns:
// - the newly-allocated iterator, which may be invalid or "past the end"
//   if the table cannot be iterated through (eg, empty).
HTIterator* HTIterator_Allocate(HashTable *table);

// When you're done with a hash table iterator, you must free it
// by calling this function.
//
// Arguments:
// - iter: the iterator to free.  Don't use it after freeing it.
void HTIterator_Free(HTIterator *iter);

// Tests to see whether the iterator is pointing at a valid element.
//
// Arguments:
// - iter: the iterator to test.
//
// Returns:
// - true: if iter is not at the end of the table (implying that
//   the table is non-empty).
// - false: if iter is past the end of the table.
//
bool HTIterator_IsValid(HTIterator *iter);

// Advance the iterator to the next element of the table.
//
// Arguments:
// - iter: the iterator to move.  Must be non-NULL.
//
// Returns:
// - true: if the iterator has been advanced to the next element.
// - false: if the iterator cannot be advanced (eg, it's "past the
//   end").  The iterator is no longer valid at this point.
bool HTIterator_Next(HTIterator *iter);

// Returns a copy of the (key,value) that the iterator is currently
// pointing at.
//
// Arguments:
// - iter: the iterator to fetch the (key,value) from.  Must be non-NULL.
// - keyvalue: a return parameter through which the (key,value)
//   is returned.
//
// Returns:
// - false: if the iterator is not valid or the table is empty.
// - true: success.
bool HTIterator_Get(HTIterator *iter, HTKeyValue_t *keyvalue);

// Returns a copy of (key,value) that the iterator is currently
// pointing at, and removes that (key,value) from the
// hashtable.  The caller assumes ownership of any memory
// pointed to by the value.  As well, this advances
// the iterator to the next element in the hashtable.
//
// Arguments:
// - iter: the iterator to fetch the (key,value) from.  Must be non-NULL.
// - keyvalue: a return parameter through which the (key,value)
//   is returned.
//
// Returns:
// - false: if the iterator is not valid or the table is empty.
// - true: successful deletion.  If there was a subsequent
//   element, the iterator has been advanced to it.  If the
//   iterator is past the end of the table, the iterator is
//   now invalid.
bool HTIterator_Remove(HTIterator *iter, HTKeyValue_t *keyvalue);

#endif  // HW1_HASHTABLE_H_