/** * @file generator.c * @author Ivaylo Ivanov 11777707 * @date 11.01.2018 * * @brief Generator program module. * * The generator program takes a graph as input. The program repeatedly generates a random solution * to the problem as described on the first page and writes its result to the circular buffer. It repeats this * procedure until it is notified by the supervisor to terminate. * The generator program takes as arguments the set of edges of the graph. * Each positional argument is one edge; at least one edge must be given. An edge is specified by a string, * with the indices of the two nodes it connects separated by a -. Since the graph is directed, the edge starts * at the first node and leads to the second node. Note that the number of nodes of the graph is implicitly * provided through the indices in the edges. In the example above the generator program is called with * the graph shown on the first page. * The generator uses the algorithm described on the first page to generate random feedback arc sets for the * given graph. It writes these feedback arc sets to the circular buffer, one at a time; therefore a feedback * arc set is a single element of the circular buffer. The generator may produce debug output, describing * the feedback arc sets which it writes to the circular buffer. * * SYNOPSIS * generator EDGE1 EDGE2 ... * * EXAMPLE * generator 0-1 1-2 1-3 1-4 2-4 3-6 4-3 4-5 6-0 * **/ #include "shared/shm.c" #include "shared/sem.c" #include "shared/structs.c" #include #include void swap(int *a, int *b); void shuffle(int arr[], size_t size); static unsigned int base_seed = 0; int main(int argc, char *argv[]) { if(argc < 2) { fprintf(stderr, "ERROR: Command takes at least one argument.\n"); puts("Usage: \ngenerator EDGE1 EDGE2 ..."); exit(EXIT_FAILURE); } if(atexit(clean_up) != 0) { fprintf(stderr, "ERROR: Cannot set exit function\n"); exit(EXIT_FAILURE); } process_signal(); /// Create a regex to check the input against regex_t regex; if(regcomp(®ex, "^[0-9]*-[0-9]*$", REG_EXTENDED|REG_NOSUB)) { fprintf(stderr, "ERROR: Could not compile regex\n"); exit(EXIT_FAILURE); } int num_edges = argc - 1; edge_t edges[num_edges]; ///< Array to hold all edges char edge[128]; ///< Variable to hold a single edge later on edge[127] = 0; int num_nodes = 0; for(int i=1; i <= num_edges; i++) { /// Check the input and terminate if incorrect if(regexec(®ex, argv[i], 0, NULL, 0) == REG_NOMATCH) { fprintf(stderr, "ERROR: Incorrect input found\n"); puts("Usage: \ngenerator EDGE1 EDGE2 ..."); exit(EXIT_FAILURE); } else { /// If input is correct, split and add to the edges array strcpy(edge, argv[i]); char *u = strtok(edge, "-"); char *v = strtok(NULL, ""); edges[i-1].u = strtol(u, NULL, 10); edges[i-1].v = strtol(v, NULL, 10); edges[i-1].chosen = false; if(num_nodes < edges[i-1].u) num_nodes = edges[i-1].u; if(num_nodes < edges[i-1].v) num_nodes = edges[i-1].u; } } num_nodes++; ///< Our first node is labeled 0, so we need to add 1 more /// Declare array for unique nodes int unique_nodes[num_nodes]; /// Filling the node array with the nodes for(int i=0; i < num_nodes; i++) { unique_nodes[i] = i; } /// Allocate resources for circular buffer int wr_pos = 0; buffer_shmfd = create_shmfd(BUFF_SHM_NAME); circ_buffer = (buffer_t *)open_shm(buffer_shmfd, sizeof(buffer_t)); /// Open semaphores free_sem = open_sem(FREE_SEM_NAME, BUFF_SIZE, 1); use_sem = open_sem(USE_SEM_NAME, 0, 1); mutex = open_sem(MUTEX_NAME, 1, 1); /// Declaring buffer arrays and variables int current_best = num_edges - 1; ///< Set a basic best solution int new_best = 0; int buf_nodes[num_nodes]; edge_t buf_edges[num_edges]; while(terminate != true && circ_buffer -> finished != true) { ///< Loop until told to stop or until an acyclic solution is discovered /// Copy the original into a buffer array and shuffle it memcpy(buf_nodes, unique_nodes, sizeof(unique_nodes)); shuffle(buf_nodes, num_nodes); /// Reset the chosen status of the edges memcpy(buf_edges, edges, sizeof(edges)); new_best = 0; ///< Reset the new_best counter for(int i=0; i < num_edges; i++) { if(new_best >= SET_MAX_SIZE || new_best >= num_edges) break; ///< Skip if the solution is faulty for(int j=0; j < num_nodes; j++) { if(buf_nodes[j] == edges[i].u) break; ///< The first node comes before the second, break else if(buf_nodes[j] == buf_edges[i].v) { buf_edges[i].chosen = true; ///< Found a correct edge, mark it as chosen new_best++; break; } } } /// Construct the new feedback arc set if(new_best < current_best && new_best <= SET_MAX_SIZE && current_best < circ_buffer -> best_arc_len) { current_best = new_best; fb_arc_set_t res; memset(&res, 0, sizeof(struct fb_arc_set)); /// Removes faulty data from the buffer later for(int i=0; i < num_edges; i++) { if(buf_edges[i].chosen == true) { res.edge_num++; res.edges[--new_best] = buf_edges[i]; ///< We begin counting from 0 } } res.valid = true; wait_sem(mutex); ///< Say that it's our turn to write wait_sem(free_sem); ///< Lock the free space semaphore /// Find a free space to write to while(circ_buffer->sets[wr_pos].valid == true) { wr_pos = (wr_pos + 1) % BUFF_SIZE; } circ_buffer->sets[wr_pos] = res; post_sem(use_sem); wr_pos = (wr_pos + 1) % BUFF_SIZE; post_sem(mutex); if(current_best == 0) { signal_handler(15); } } } exit(EXIT_SUCCESS); } /** * @brief Function that swaps a two variables * @details The function takes pointers to two variables and swaps their values with a buffer variable * @param *a, *b * @return none **/ void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } /** * @brief Function that shuffles a given array * @details The function takes an array and shuffles it randomly using the Fisher-Yates algorithm: * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle * @param arr, size * @return res **/ void shuffle(int arr[], size_t size) { base_seed = (base_seed + 1) % (__UINT64_MAX__); ///< Extreme values for the randomizer srand(base_seed); ///< Set a random seed int j = 0; ///< Index that will be randomized later on for(int i = size - 1; i > 0; i--) { j = rand()%(i+1); ///< Pick a random index swap(&arr[i], &arr[j]); ///< Swap the values } }