485 lines
12 KiB
C
485 lines
12 KiB
C
#include <stdio.h>
|
|
#include <array_2d.h> // ska innehålla array_2d implementation
|
|
#include <graph.h>
|
|
#include <dlist.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
* 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");
|
|
}
|
|
}
|