This repository has been archived on 2021-08-17. You can view files and clone it, but cannot push or open issues or pull requests.
unix/cpair/cpair.c

296 lines
9.1 KiB
C
Raw Normal View History

2018-12-08 21:34:47 +00:00
/**
* @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.
2018-12-08 21:34:47 +00:00
*
* 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>
2018-12-12 21:06:56 +00:00
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
2018-12-08 21:34:47 +00:00
2018-12-12 21:06:56 +00:00
/// 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
*
**/
2018-12-12 21:06:56 +00:00
float points[2];
struct node * next;
} node_t;
2018-12-08 21:34:47 +00:00
2018-12-12 21:06:56 +00:00
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);
2018-12-12 21:06:56 +00:00
int main(void) {
2018-12-08 21:34:47 +00:00
char input[__UINT8_MAX__] = ""; ///< An array to save the input to
int point_num = 0; ///< Save the number of points
2018-12-12 21:06:56 +00:00
node_t * head = NULL;
head = malloc(sizeof(node_t));
node_t * current = head;
2018-12-08 21:34:47 +00:00
while(fgets(input, __INT8_MAX__, stdin) != NULL) { ///< Read line by line
/// Split the input by whitespace as delimiter
char *x = strtok(input, " ");
char *y = strtok(NULL, " ");
if(x != NULL && y != NULL) {
2018-12-12 21:06:56 +00:00
/// 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
2018-12-08 21:34:47 +00:00
} else {
fprintf(stderr, "ERROR: Ill-formed line found\n");
2018-12-08 21:34:47 +00:00
exit(EXIT_FAILURE);
}
}
if(feof(stdin) == 0) {
fprintf(stderr, "ERROR: An error interrupted the read\n");
2018-12-08 21:34:47 +00:00
exit(EXIT_FAILURE);
}
if(point_num == 1)
exit(EXIT_SUCCESS);
if(point_num == 2) {
2018-12-12 21:06:56 +00:00
printf("%f %f\n", head -> points[0], head -> points[1]);
printf("%f %f\n", head -> next -> points[0], head -> next -> points[1]);
2018-12-08 21:34:47 +00:00
exit(EXIT_SUCCESS);
}
2018-12-12 21:06:56 +00:00
float x_mean = get_x_mean(head);
node_t * first_part = malloc(sizeof(node_t));
node_t * second_part = malloc(sizeof(node_t));
2018-12-12 21:06:56 +00:00
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;
} else {
second_part_buff -> points[0] = current -> points[0];
second_part_buff -> next = malloc(sizeof(node_t));
second_part_buff = second_part_buff -> next;
}
current = current -> next;
2018-12-12 21:06:56 +00:00
}
/**
* Create the pipe file descriptors and inititalize the pipes
*
* fd[0] - input side of pipe
* fd[1] - output side of pipe
*
**/
int pipe_a[2], pipe_b[2];
if(pipe(pipe_a) < 0 || pipe(pipe_b) < 0) {
fprintf(stderr, "ERROR: Failed creating the pipe\n");
exit(EXIT_FAILURE);
}
pid_t child_a, child_b; ///< Create variables for the child processes
node_t * child_a_part = NULL;
node_t * child_b_part = NULL;
write(pipe_a[0], first_part, sizeof(node_t)); ///< Send the first part of the array to the child
switch(child_a = fork()) {
2018-12-12 21:06:56 +00:00
case -1:
fprintf(stderr ,"ERROR: Couldn't fork child\n");
2018-12-12 21:06:56 +00:00
exit(EXIT_FAILURE);
case 0:
if(read(pipe_a[0], child_a_part, sizeof(node_t)) < 0) {
fprintf(stderr, "ERROR: Failed reading from pipe\n");
exit(EXIT_FAILURE);
}
if(write(pipe_a[1], find_shortest_distance(child_a_part), sizeof(node_t)) < 0) {
fprintf(stderr, "ERROR: Failed writing to pipe\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
2018-12-12 21:06:56 +00:00
break;
default:
write(pipe_b[0], second_part, sizeof(node_t));
switch(child_b = fork()) {
case -1:
fprintf(stderr, "ERROR: Couldn't fork child\n");
exit(EXIT_FAILURE);
case 0:
if(read(pipe_b[0], child_b_part, sizeof(node_t)) < 0) {
fprintf(stderr, "ERROR: Failed reading from pipe\n");
exit(EXIT_FAILURE);
}
if(write(pipe_b[1], find_shortest_distance(child_b_part), sizeof(node_t)) < 0) {
fprintf(stderr, "ERROR: Failed writing to pipe\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
break;
default:
break;
}
2018-12-12 21:06:56 +00:00
break;
}
if(wait_for_termination(child_a) != EXIT_SUCCESS)
exit(EXIT_FAILURE);
if(wait_for_termination(child_b) != EXIT_SUCCESS)
exit(EXIT_FAILURE);
2018-12-12 21:06:56 +00:00
/// Free the memory
free(head);
free(first_part);
free(first_part_buff);
free(second_part);
free(second_part_buff);
free(current);
2018-12-08 21:34:47 +00:00
exit(EXIT_SUCCESS);
}
/**
2018-12-12 21:06:56 +00:00
* @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
2018-12-08 21:34:47 +00:00
* @return mean of all X coordinates
*
**/
2018-12-12 21:06:56 +00:00
float get_x_mean(node_t * head) {
node_t * current = head;
2018-12-08 21:34:47 +00:00
float x_sum = 0;
int i = 1;
while(current != NULL) {
2018-12-12 21:06:56 +00:00
x_sum += current -> points[0];
current = current -> next;
i++;
2018-12-08 21:34:47 +00:00
}
2018-12-12 21:06:56 +00:00
return x_sum/(i - 2); ///< TODO: Fix the bug with the 2 more iterations
2018-12-12 21:06:56 +00:00
}
/**
* @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);
}