/** * @file cpair.c * @author Ivaylo Ivanov 11777707 * @date 08.12.2018 * * @brief Main program module. * * A program that searches for the closest pair of points in a set of 2D-points * * SYNOPSIS * cpair * * EXAMPLE * $ cat 1.txt * 4.0 4.0 * -1.0 1.0 * 1.0 -1.0 * -4.0 -4.0 * $ ./cpair < 1.txt * -1.000000 1.000000 * 1.000000 -1.000000 * * * The program accepts an array of 2D-points as an input from stdin. The input ends at EOF. * * The program does the following: * - No output if the array has one point * - The points if the array has 2 points * - Otherwise, the array is split in 2 parts based on the mean of X and sent to 2 different paralell child processes. * The parent watches for the return code of the children and terminates with an error if any of the children exit * with anything other than success. * * Algorithm description when there are more than 2 points: * - P1 and P2 are the closest pairs for the first and the second part respectively. * - Go through all of the pairs between the points from the first half with the second half and save the shortest one in P3. * - Compare P1, P2 and P3 and return the shortest one to stdout. * **/ #include #include #include #include #include #include #include /// Node struct for linked list typedef struct node { /** * points[0] - contains the X coordinate of the point * points[1] - contains the Y coordinate of the point * **/ float points[2]; struct node * next; } node_t; float get_x_mean(node_t * head); float get_distance(float x1, float x2, float y1, float y2); node_t * find_shortest_distance(node_t * list); int wait_for_termination(pid_t child); int main(int argc, char *argv[]) { if (argc > 1) { puts("The command takes no additional arguments."); puts("Usage: cpair"); exit(EXIT_FAILURE); } /** * Create the pipe file descriptors and inititalize the pipes * * fd[0] - input side of pipe * fd[1] - output side of pipe * **/ int in_pipe_a[2], out_pipe_a[2], in_pipe_b[2], out_pipe_b[2]; if(pipe(in_pipe_a) < 0 || pipe(in_pipe_b) < 0 || pipe(out_pipe_a) < 0 || pipe(out_pipe_b) < 0) { fprintf(stderr, "ERROR: Failed creating the pipes\n"); exit(EXIT_FAILURE); } char input[__UINT8_MAX__] = ""; ///< An array to save the input to int point_num = 0; ///< Save the number of points node_t * head = NULL; head = malloc(sizeof(node_t)); node_t * current = head; while(fgets(input, __INT8_MAX__, stdin) != NULL) { ///< Read line by line char *sep, *end; float x = strtof(input, &sep); ///< if no characters were converted then input will point to end float y = strtof(sep, &end); if(input != sep || sep != end) { /// Convert to float and save to the list current -> points[0] = x; current -> points[1] = y; current -> next = malloc(sizeof(node_t)); current = current -> next; point_num++; ///< Increase the list length } else { fprintf(stderr, "ERROR: Ill-formed line found\n"); exit(EXIT_FAILURE); } } if(feof(stdin) == 0) { fprintf(stderr, "ERROR: An error interrupted the read\n"); exit(EXIT_FAILURE); } if(point_num == 1) exit(EXIT_SUCCESS); if(point_num == 2) { printf("%f %f\n", head -> points[0], head -> points[1]); printf("%f %f\n", head -> next -> points[0], head -> next -> points[1]); exit(EXIT_SUCCESS); } pid_t child_a, child_b; ///< Create variables for the child processes /// Create first child switch(child_a = fork()) { case -1: fprintf(stderr ,"ERROR: Couldn't fork child\n"); exit(EXIT_FAILURE); case 0: /// Child execution close(out_pipe_a[0]); close(in_pipe_a[1]); close(STDIN_FILENO); if(dup2(in_pipe_a[0], STDIN_FILENO) != STDIN_FILENO) { fprintf(stderr, "ERROR: Failed duplicating STDIN"); exit(EXIT_FAILURE); } execlp(argv[0], argv[0], NULL); fprintf(stderr, "ERROR: Failed to load program into child"); exit(EXIT_FAILURE); default: /// Parent execution /// Create second child switch(child_b = fork()) { case -1: fprintf(stderr, "ERROR: Couldn't fork child\n"); exit(EXIT_FAILURE); case 0: /// Child execution close(out_pipe_b[0]); close(in_pipe_b[1]); close(STDIN_FILENO); if(dup2(in_pipe_b[0], STDIN_FILENO) != STDIN_FILENO) { fprintf(stderr, "ERROR: Failed duplicating STDIN"); exit(EXIT_FAILURE); } execlp(argv[0], argv[0], NULL); fprintf(stderr, "ERROR: Failed to load program into child"); exit(EXIT_FAILURE); default: /// Parent execution close(in_pipe_a[0]); close(in_pipe_b[0]); close(STDOUT_FILENO); float x_mean = get_x_mean(head); node_t * first_part = malloc(sizeof(node_t)); int first_part_len = 1; node_t * second_part = malloc(sizeof(node_t)); int second_part_len = 1; node_t * first_part_buff = first_part; node_t * second_part_buff = second_part; /// Separate the lists based on the mean current = head; while(current != NULL) { if(current -> points[0] <= x_mean) { first_part_buff -> points[0] = current -> points[0]; first_part_buff -> next = malloc(sizeof(node_t)); first_part_buff = first_part_buff -> next; first_part_len ++; } else { second_part_buff -> points[0] = current -> points[0]; second_part_buff -> next = malloc(sizeof(node_t)); second_part_buff = second_part_buff -> next; second_part_len ++; } current = current -> next; } /** * * Send first half to first child * **/ if(dup2(in_pipe_a[1], STDIN_FILENO) != STDIN_FILENO) { fprintf(stderr, "ERROR: Failed duplicating STDIN"); exit(EXIT_FAILURE); } /// Setup string for pipe current = first_part; char first_part_string[first_part_len + 1][__UINT8_MAX__]; int i = 0; while(current != NULL) { sprintf(first_part_string[i], "%f %f\n", current -> points[0], current -> points[1]); i++; current = current -> next; } first_part_string[first_part_len][0] = EOF; /// Write to pipe if (write(in_pipe_a[1], first_part_string, first_part_len * __UINT8_MAX__) < 0) { fprintf(stderr, "ERROR: Failed writing to child\n"); exit(EXIT_FAILURE); } close(STDOUT_FILENO); /** * * Send second half to second child * **/ if(dup2(in_pipe_b[1], STDIN_FILENO) != STDIN_FILENO) { fprintf(stderr, "ERROR: Failed duplicating STDIN\n"); exit(EXIT_FAILURE); } /// Setup string for pipe current = second_part; char second_part_string[second_part_len + 1][__UINT8_MAX__]; i = 0; while(current != NULL) { sprintf(second_part_string[i], "%f %f\n", current -> points[0], current -> points[1]); i++; current = current -> next; } second_part_string[second_part_len][0] = EOF; /// Write to pipe if (write(in_pipe_b[1], second_part_string, second_part_len * __UINT8_MAX__) < 0) { fprintf(stderr, "ERROR: Failed writing to child\n"); exit(EXIT_FAILURE); } close(STDOUT_FILENO); /// Wait for the correct exit of the children if(wait_for_termination(child_a) != EXIT_SUCCESS || wait_for_termination(child_b) != EXIT_SUCCESS) { fprintf(stderr, "ERROR: Children didn't terminate successfully\n"); exit(EXIT_FAILURE); } /// Free the memory free(head); free(first_part); free(first_part_buff); free(second_part); free(second_part_buff); free(current); break; } } exit(EXIT_SUCCESS); } /** * @brief Function to calculate the mean of the X coordinates from a points list * @details Loops through the points list, gets the sum of X coordinates and returns the mean * @param head - a pointer to a list with the points * @return mean of all X coordinates * **/ float get_x_mean(node_t * head) { node_t * current = head; float x_sum = 0; int i = 1; while(current != NULL) { x_sum += current -> points[0]; current = current -> next; i++; } return x_sum/(i - 2); ///< TODO: Fix the bug with the 2 more iterations } /** * @brief Function to calculate the distance between 2 points * @details The function calculates the distance between 2 points * in a Cartesian coordinate system, given their coordinates * @param x1, y1 - pair of coordinates for the first point * x2, y2 - pair of coordinates for the second point * @return distance between the two points * **/ float get_distance(float x1, float x2, float y1, float y2) { return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)); } /** * @brief Function to find the shortest distance in a list * @details The function gets a list of points and finds the pair * with the shortest distance between them. * @param list - the list with points * @return res - a list containing the coordinates of the two closest points * **/ node_t * find_shortest_distance(node_t * list) { node_t * current = list; node_t * next = list; /// Setup the result list node_t * res = malloc(sizeof(node_t)); res -> next = malloc(sizeof(node_t)); int current_shortest = 0; int new_shortest = 0; while(current != NULL) { next = current -> next; if(next != NULL) { new_shortest = get_distance(current -> points[0], next -> points[0], current -> points[1], next -> points[1]); if(new_shortest < current_shortest) { current_shortest = new_shortest; /// Save the pairs res -> points[0] = current -> points[0]; res -> points[1] = current -> points[1]; res -> next -> points[0] = next -> points[0]; res -> next -> points[1] = next -> points[1]; } current = next; } else break; } return res; } /** * @briefs Function that waits for a child to close * @details The function waits for a child to close. * If it closes successfully, it returns the exit status only. * Otherwise, it prints an error message and then returns the exit status. * @param child - the pid of the child process * @return the exist status of the child process * **/ int wait_for_termination(pid_t child) { int exit_stat = 0; if(waitpid(child, &exit_stat, 0) < 0) { fprintf(stderr, "ERROR: Child did not exit successfully\n"); } return WEXITSTATUS(exit_stat); }