#include #include // ska innehålla array_2d implementation #include #include #include #include /* * graph.c: Implementation of a 2-dimensional array based graph for "Obligatorisk uppgift 4" in the "Datastructures and * algorithms" course at the Department of Computing Science, Umea University. * * Authors: Jakob Nyström tfy22jnm * Ellen Horneij tfy22ehj * Marc Meunier tfy22mmr * * Version information: * v1.0 2025-05-25: First public version. * */ // ===========INTERNAL DATA TYPES ============ // Graph contains the matrix with nodes and edges, total number of nodes ans edges and struct graph { array_2d *entries; int nr_of_nodes; int nr_of_edges; int max_nodes; }; /** * Node entry contains the value of the node, it's position and it's seen status */ struct node { const char *value; int position; bool seen; }; // =================== NODE COMPARISON FUNCTION ====================== /** * nodes_are_equal() - Check whether two nodes are equal. * @n1: Pointer to node 1. * @n2: Pointer to node 2. * * Returns: true if the nodes are considered equal, otherwise false. * */ bool nodes_are_equal(const node *n1, const node *n2) { // Uses strcomp to compare the nodes int cmp = strcmp(n1->value, n2->value); // Returns true if strcomp returns 0 meaning that the nodes are equal, if else false if (cmp == 0) { return true; } else { return false; } } // ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ /** * node_create() - Allocates the memory and creates a node entry * @s: the value of the node * * Returns: The pointer to node entry with value s */ node *node_create(const char *s) { // Allocate the memory for the node entry node *n = calloc(1, sizeof(*n)); // Populate the entry. n->seen = false; char *tmp = malloc(strlen(s) + 1); if (tmp != NULL) { strcpy(tmp, s); n->value = tmp; } else { // Handle memory allocation failure free(n); return NULL; } return n; } /** * table_entry_kill() - Return the memory allocated to a table entry. * @e: The table entry to deallocate. * * Returns: Nothing. */ void node_kill(node *v) { //node *n = v; // Convert the pointer (useful if debugging the code) if (v == NULL) { return; // Nothing to free if v is NULL } // free strings free((void*)v->value); // All we need to do is to deallocate the struct. free(v); } // =================== GRAPH STRUCTURE INTERFACE ====================== /** * graph_empty() - Create an empty graph. * @max_nodes: The maximum number of nodes the graph can hold. * * Returns: A pointer to the new graph */ graph *graph_empty(int max_nodes) { // Allocate the graph memory graph *g = calloc(1, sizeof(graph)); // Create the array for the entries g->entries = array_2d_create(0, max_nodes, 0, max_nodes + 1, NULL); g->nr_of_nodes = 0; g->nr_of_edges = 0; g->max_nodes = max_nodes; return g; } /** * graph_is_empty() - Check if a graph is empty, i.e. has no nodes. * @g: Graph to check. * * Returns: True if graph is empty, otherwise false. */ bool graph_is_empty(const graph *g) { // Returns true if nr_of_nodes is empty return g->nr_of_nodes == 0; } /** * graph_has_edges() - Check if a graph has any edges. * @g: Graph to check. * * Returns: True if graph has any edges, otherwise false. */ bool graph_has_edges(const graph *g) { // Returns true if nr_of_edges is not empty return g->nr_of_edges != 0; } /** * graph_insert_node() - Inserts a node with the given name into the graph. * @g: Graph to manipulate. * @s: Node name. * * Creates a new node with a copy of the given name and puts it into * the graph. * * Returns: The modified graph. */ graph *graph_insert_node(graph *g, const char *s) { // Define variables and create a node entry with s for comparisons int index = 0; int count = 0; node *m = node_create(s); // Check for duplicates among the already taken slots while (index < g->max_nodes && count < g->nr_of_nodes) { // Inspect the table entry node *n = array_2d_inspect_value(g->entries, index, 0); // Check if the node already exists if (n != NULL && nodes_are_equal(n, m)) { // If the node is a duplicate, exit the loop and skips the insertion loop node_kill(m); return g; } if (n != NULL) { // Update how many taken slots that have been checked count++; } // Continue with the next position. index++; } // If no duplicate is found, insert the node in the first available slot while (index < g->max_nodes) { // Check for empty slot node *n = array_2d_inspect_value(g->entries, index, 0); if (n == NULL) { // Input node in the empty slot and update variables before exiting the loop m->position = index; m->seen = false; array_2d_set_value(g->entries, m, index, 0); g->nr_of_nodes++; break; } index++; } // Return the modified graph return g; } /** * graph_find_node() - Find a node stored in the graph. * @g: Graph to manipulate. * @s: Node identifier, e.g. a char *. * * Returns: A pointer to the found node, or NULL. */ node *graph_find_node(const graph *g, const char *s) { int index = 0; int count = 0; node *m = node_create(s); // Loop over the node list while (index < g->max_nodes && count < g->nr_of_nodes) { // Check if a node with value s exists and return it, if not continue to next node node *n = array_2d_inspect_value(g->entries, index, 0); if (n != NULL && nodes_are_equal(n, m)) { // Deallocate comparison node node_kill(m); return n; } if (n != NULL) { count++; } index++; } // If no match is found, deallocate comparison node and return NULL node_kill(m); return NULL; } /** * graph_node_is_seen() - Return the seen status for a node. * @g: Graph storing the node. * @n: Node in the graph to return seen status for. * * Returns: The seen status for the node. */ bool graph_node_is_seen(const graph *g, const node *n) { // Check if node exists and return its seen-status node *m = graph_find_node(g, n->value); if (m != NULL) { return m->seen; } // Return NULL if the node is not found return false; } /** * graph_node_set_seen() - Set the seen status for a node. * @g: Graph storing the node. * @n: Node in the graph to set seen status for. * @s: Status to set. * * Returns: The modified graph. */ graph *graph_node_set_seen(graph *g, node *n, bool seen) { // Check if node exists and update its seen-status node *m = graph_find_node(g, n->value); if (m != NULL) { m->seen = seen; } // Return the modified graph return g; } /** * graph_reset_seen() - Reset the seen status on all nodes in the graph. * @g: Graph to modify. * * Returns: The modified graph. */ graph *graph_reset_seen(graph *g) { int index = 0; int count = 0; // Iterates over all nodes and sets the seen-status to FALSE while (index < g->max_nodes && count < g->nr_of_nodes) { node *n = array_2d_inspect_value(g->entries, index, 0); if (n != NULL) { n->seen = false; count++; } index++; } return g; } /** * graph_insert_edge() - Insert an edge into the graph. * @g: Graph to manipulate. * @n1: Source node (pointer) for the edge. * @n2: Destination node (pointer) for the edge. * * NOTE: Undefined unless both nodes are already in the graph. * * Returns: The modified graph. */ graph *graph_insert_edge(graph *g, node *n1, node *n2) { // Checks if both source and destination exists if (graph_find_node(g, n1->value) != NULL && graph_find_node(g, n2->value) != NULL) { // Inserts n2 into the edge list of n1 and updates the nr_of_edges array_2d_set_value(g->entries, n2, n1->position, n2->position + 1); g->nr_of_edges++; } return g; } /** * graph_delete_node() - Remove a node from the graph. * @g: Graph to manipulate. * @n: Node to remove from the graph. * * Returns: The modified graph. * * NOTE: Undefined if the node is not in the graph. */ graph *graph_delete_node(graph *g, node *n) { // Removes the node and all of its destinations for (int i = 0; i < g->max_nodes; i++) { array_2d_set_value(g->entries, NULL, n->position, 1); } // Updatess the nr_of_nodes and returns the modified graph g->nr_of_nodes--; return g; } /** * graph_delete_edge() - Remove an edge from the graph. * @g: Graph to manipulate. * @n1: Source node (pointer) for the edge. * @n2: Destination node (pointer) for the edge. * * Returns: The modified graph. * * NOTE: Undefined if the edge is not in the graph. */ graph *graph_delete_edge(graph *g, node *n1, node *n2) { // Removes n2 from n1's destination list array_2d_set_value(g->entries, NULL, n1->position, n2->position + 1); g->nr_of_edges--; return g; } /** * graph_neighbours() - Return a list of neighbour nodes. * @g: Graph to inspect. * @n: Node to get neighbours for. * * Returns: A pointer to a list of nodes. Note: The list must be * dlist_kill()-ed after use. */ dlist *graph_neighbours(const graph *g, const node *n) { // Creates the return list and gets its first position dlist *l = dlist_empty(NULL); dlist_pos pos = dlist_first(l); if (n != NULL) { for (int i = 1; i <= g->max_nodes + 1; i++) { // If a destination exists add it to the list of neigbours and update the list position node *m = array_2d_inspect_value(g->entries, n->position, i); if (m != NULL) { dlist_insert(l, m, pos); pos = dlist_next(l, pos); } } } // Return the list of neigbours return l; } /** * graph_kill() - Destroy a given graph. * @g: Graph to destroy. * * Return all dynamic memory used by the graph. * * Returns: Nothing. */ void graph_kill(graph *g) { // Iterate over the list of nodes and deallocate them for (int i = 0; i < g->max_nodes; i++) { node *n = array_2d_inspect_value(g->entries, i, 0); node_kill(n); } // free the array and the graph array_2d_kill(g->entries); free(g); } /** * graph_print() - Iterate over the graph elements and print their values. * @g: Graph to inspect. * * Iterates over the graph and prints its contents. * * Returns: Nothing. */ void graph_print(const graph *g) { // Itirate over the list and print the value for (int i = 0; i < g->max_nodes; i++) { for (int j = 0; j < g->max_nodes + 1; j++) { node *n = array_2d_inspect_value(g->entries, i, j); // Check if there is a value to print if (n != NULL && j == 0) { // Print the value printf("[%s] -> ", n->value); } else if (j == 0) { // Print no value printf("[###] -> "); } else if (n != NULL) { // Print the value printf("[%s] ", n->value); } else { // Print no value printf("[###] "); } } printf("\n"); } }