446 lines
11 KiB
C
446 lines
11 KiB
C
/* 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: Can’t 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: Can’t 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: Can’t 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;
|
||
} |