335 lines
11 KiB
C
335 lines
11 KiB
C
/**
|
|
* @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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
/// 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 = ""; ///< An array to save the input to
|
|
size_t input_len;
|
|
int point_num = 0; ///< Save the number of points
|
|
node_t * head = NULL;
|
|
|
|
head = malloc(sizeof(node_t));
|
|
|
|
node_t * current = head;
|
|
|
|
while(getline(&input, &input_len, stdin) > 0) { ///< Read line by line
|
|
fprintf(stderr, "pid: %d \n%s\n", getpid(), input);
|
|
/// Split the input by whitespace as delimiter
|
|
char *x = strtok(input, " ");
|
|
char *y = strtok(NULL, " ");
|
|
|
|
if(x != NULL && y != NULL) {
|
|
/// Convert to float and save to the list
|
|
current -> points[0] = strtof(x, NULL);
|
|
current -> points[1] = strtof(y, NULL);
|
|
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(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\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
execlp(argv[0], argv[0], NULL);
|
|
|
|
fprintf(stderr, "ERROR: Failed to load program into child\n");
|
|
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\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
execlp(argv[0], argv[0], NULL);
|
|
|
|
fprintf(stderr, "ERROR: Failed to load program into child\n");
|
|
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);
|
|
|
|
/// Separate the lists based on the mean
|
|
current = head;
|
|
|
|
char * first_part = malloc(point_num * __UINT8_MAX__);
|
|
char * second_part = malloc(point_num * __UINT8_MAX__);
|
|
|
|
while(current -> next != NULL) {
|
|
char point[__UINT8_MAX__];
|
|
if(current -> points[0] <= x_mean) {
|
|
/// Setup string for first child pipe
|
|
sprintf(point, "%f %f\n", current -> points[0], current -> points[1]);
|
|
strcat(first_part, point);
|
|
} else {
|
|
/// Setup string for second child pipe
|
|
sprintf(point, "%f %f\n", current -> points[0], current -> points[1]);
|
|
strcat(second_part, point);
|
|
}
|
|
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\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/// Write to pipe
|
|
if (write(in_pipe_a[1], first_part, strlen(first_part)) < 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);
|
|
}
|
|
|
|
/// Write to pipe
|
|
if (write(in_pipe_b[1], second_part, strlen(second_part)) < 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(second_part);
|
|
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);
|
|
}
|