cmkl/spring-2025/sen-107/00050/cmkl_book.c

446 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* cmkl_book
*
* Build a social network with basic functionalities
*
* Template for Fundamental Data Structures Lab 2
* Modified by Thanawin Pattanaphol, 2025-05-02
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_ACCOUNTS 1000 // Arbitrary limit for simplicity
#define MAX_FRIENDS 100 // Arbitrary limit for simplicity
// Structure for an account
typedef struct Account
{
char emailAddress[55];
char firstName[25];
char lastName[25];
bool isActive;
int numFriends;
struct Account *friends[MAX_FRIENDS];
} Account;
// Hash table structure
#define HASH_TABLE_SIZE 101 // A prime number for better distribution
typedef struct HashNode
{
char emailAddress[55];
Account *account;
struct HashNode *next;
} HashNode;
HashNode *accountHashTable[HASH_TABLE_SIZE];
int numAccounts = 0;
Account accounts[MAX_ACCOUNTS];
// Hash function
unsigned long hash(const char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash % HASH_TABLE_SIZE;
}
// Find an account by email in the hash table
Account *findAccount(const char *emailAddress)
{
unsigned long index = hash(emailAddress);
HashNode *current = accountHashTable[index];
while (current != NULL)
{
if (strcmp(current->emailAddress, emailAddress) == 0 && current->account->isActive)
{
return current->account;
}
current = current->next;
}
return NULL;
}
// Insert an account into the hash table
void insertAccount(Account *account)
{
unsigned long index = hash(account->emailAddress);
HashNode *newNode = (HashNode *)malloc(sizeof(HashNode));
if (newNode == NULL)
{
perror("Failed to allocate memory for hash node");
exit(EXIT_FAILURE);
}
strcpy(newNode->emailAddress, account->emailAddress);
newNode->account = account;
newNode->next = accountHashTable[index];
accountHashTable[index] = newNode;
}
// Delete an account from the hash table (mark as inactive)
Account *deleteAccountFromTable(const char *emailAddress)
{
unsigned long index = hash(emailAddress);
HashNode *current = accountHashTable[index];
HashNode *prev = NULL;
while (current != NULL)
{
if (strcmp(current->emailAddress, emailAddress) == 0 && current->account->isActive)
{
current->account->isActive = false;
return current->account;
}
prev = current;
current = current->next;
}
return NULL;
}
// Add a new account
void newAccount(char *emailAddress, char *firstName, char *lastName)
{
if (numAccounts >= MAX_ACCOUNTS)
{
fprintf(stderr, "Error: Maximum number of accounts reached.\n");
return;
}
if (findAccount(emailAddress) != NULL) {
printf("Error: Duplicate\n");
return;
}
Account *newAccountPtr = &accounts[numAccounts++];
strcpy(newAccountPtr->emailAddress, emailAddress);
strcpy(newAccountPtr->firstName, firstName);
strcpy(newAccountPtr->lastName, lastName);
newAccountPtr->isActive = true;
newAccountPtr->numFriends = 0;
insertAccount(newAccountPtr);
printf("Success\n");
}
// Delete an account
void deleteAccount(char *emailAddress)
{
Account *accountToDelete = deleteAccountFromTable(emailAddress);
if (accountToDelete == NULL)
{
printf("Error: Not Found\n");
return;
}
// Remove this account from the friend lists of others
for (int i = 0; i < numAccounts; i++)
{
if (accounts[i].isActive)
{
Account *friendAccount = &accounts[i];
for (int j = 0; j < friendAccount->numFriends; j++)
{
if (friendAccount->friends[j] == accountToDelete)
{
// Shift remaining friends to the left
for (int k = j; k < friendAccount->numFriends - 1; k++)
{
friendAccount->friends[k] = friendAccount->friends[k + 1];
}
friendAccount->numFriends--;
break; // Only one instance of the deleted friend
}
}
}
}
printf("Success %s %s\n", accountToDelete->firstName, accountToDelete->lastName);
}
// Make two accounts friends
void addFriend(char *firstEmailAddress, char *secondEmailAddress)
{
Account *account1 = findAccount(firstEmailAddress);
Account *account2 = findAccount(secondEmailAddress);
if (account1 == NULL || account2 == NULL)
{
printf("Error: Account Not Found\n");
return;
}
if (strcmp(firstEmailAddress, secondEmailAddress) == 0)
{
printf("Error: Cant add friend to self\n");
return;
}
bool areFriends = false;
for (int i = 0; i < account1->numFriends; i++)
{
if (account1->friends[i] == account2)
{
areFriends = true;
break;
}
}
if (areFriends)
{
printf("Error: Accounts are already friends\n");
return;
}
if (account1->numFriends < MAX_FRIENDS && account2->numFriends < MAX_FRIENDS)
{
account1->friends[account1->numFriends++] = account2;
account2->friends[account2->numFriends++] = account1;
printf("Success\n");
}
else
{
fprintf(stderr, "Error: Maximum number of friends reached for one of the accounts.\n");
}
}
// Unfriend two accounts
void unfriend(char *firstEmailAddress, char *secondEmailAddress)
{
Account *account1 = findAccount(firstEmailAddress);
Account *account2 = findAccount(secondEmailAddress);
if (account1 == NULL || account2 == NULL)
{
printf("Error: Account Not Found\n");
return;
}
if (strcmp(firstEmailAddress, secondEmailAddress) == 0)
{
printf("Error: Cant unfriend self\n");
return;
}
int index1 = -1, index2 = -1;
for (int i = 0; i < account1->numFriends; i++)
{
if (account1->friends[i] == account2)
{
index1 = i;
break;
}
}
for (int i = 0; i < account2->numFriends; i++)
{
if (account2->friends[i] == account1)
{
index2 = i;
break;
}
}
if (index1 == -1 || index2 == -1)
{
printf("Error: Cant unfriend accounts that are not friends\n");
return;
}
// Remove from account1's friend list
for (int i = index1; i < account1->numFriends - 1; i++)
{
account1->friends[i] = account1->friends[i + 1];
}
account1->numFriends--;
// Remove from account2's friend list
for (int i = index2; i < account2->numFriends - 1; i++)
{
account2->friends[i] = account2->friends[i + 1];
}
account2->numFriends--;
printf("Success\n");
}
// List all friends of an account
void listFriends(char *emailAddress)
{
Account *account = findAccount(emailAddress);
if (account == NULL)
{
printf("Error: Account Not Found\n");
return;
}
if (account->numFriends == 0)
{
printf("No Friend\n");
return;
}
for (int i = 0; i < account->numFriends; i++)
{
printf("%s %s", account->friends[i]->firstName, account->friends[i]->lastName);
if (i < account->numFriends - 1)
{
printf(", ");
}
}
printf("\n");
}
// List friend suggestions for an account
void listSuggestions(char *emailAddress)
{
Account *account = findAccount(emailAddress);
if (account == NULL)
{
printf("Error: Account Not Found\n");
return;
}
if (account->numFriends == 0)
{
printf("No Friend Suggestion\n");
return;
}
Account *suggestions[MAX_ACCOUNTS];
int numSuggestions = 0;
bool isSuggested;
for (int i = 0; i < account->numFriends; i++)
{
Account *friend = account->friends[i];
for (int j = 0; j < friend->numFriends; j++)
{
Account *potentialFriend = friend->friends[j];
// Don't suggest self or existing friends
if (potentialFriend == account) continue;
bool isAlreadyFriend = false;
for (int k = 0; k < account->numFriends; k++)
{
if (account->friends[k] == potentialFriend)
{
isAlreadyFriend = true;
break;
}
}
if (isAlreadyFriend) continue;
// Check if already suggested
isSuggested = false;
for (int k = 0; k < numSuggestions; k++)
{
if (suggestions[k] == potentialFriend)
{
isSuggested = true;
break;
}
}
if (!isSuggested && potentialFriend->isActive)
{
suggestions[numSuggestions++] = potentialFriend;
}
}
}
if (numSuggestions == 0)
{
printf("No Friend Suggestion\n");
return;
}
for (int i = 0; i < numSuggestions; i++)
{
printf("%s %s", suggestions[i]->firstName, suggestions[i]->lastName);
if (i < numSuggestions - 1)
{
printf(", ");
}
}
printf("\n");
}
// Free the contents of the datastructures used
void freeAll()
{
// Accounts array is statically allocated, no need to free individually
// Free hash table nodes
for (int i = 0; i < HASH_TABLE_SIZE; i++)
{
HashNode *current = accountHashTable[i];
while (current != NULL)
{
HashNode *temp = current;
current = current->next;
free(temp);
}
accountHashTable[i] = NULL;
}
numAccounts = 0;
}
int main() {
char input[64]; // Input operation name
char emailAddress[55]; // Email address of a person
char secondEmailAddress[55]; // Email address of another person
char firstName[25]; // First name of the person
char lastName[25]; // First name of the person
int numOperations; // The number of operations.
// Initialize hash table
for (int i = 0; i < HASH_TABLE_SIZE; i++)
{
accountHashTable[i] = NULL;
}
scanf("%d", &numOperations);
for (int i = 0; i < numOperations; i++)
{
scanf("\n%s", input);
if (strcmp(input, "New") == 0)
{
scanf(" %s %s %s", emailAddress, firstName, lastName);
newAccount(emailAddress, firstName, lastName);
}
else if (strcmp(input, "Delete") == 0)
{
scanf(" %s", emailAddress);
deleteAccount(emailAddress);
}
else if (strcmp(input, "Add") == 0)
{
scanf(" %s %s", emailAddress, secondEmailAddress);
addFriend(emailAddress, secondEmailAddress);
}
else if (strcmp(input, "Unfriend") == 0)
{
scanf(" %s %s", emailAddress, secondEmailAddress);
unfriend(emailAddress, secondEmailAddress);
}
else if (strcmp(input, "Friend") == 0)
{
scanf(" %s", emailAddress);
listFriends(emailAddress);
}
else if (strcmp(input, "Suggestion") == 0)
{
scanf(" %s", emailAddress);
listSuggestions(emailAddress);
}
}
freeAll();
return 0;
}