├── DiseaseAggregator ├── aggregator │ ├── AggregatorMenu.c │ ├── Makefile │ ├── aggregator.c │ ├── main.c │ └── stats_parser.c ├── create_executable.sh ├── create_files.sh ├── include │ ├── BalancedTree.h │ ├── BinaryTree.h │ ├── CompareFunctions.h │ ├── Dates.h │ ├── GlobalStructures.h │ ├── HashTable.h │ ├── HeapUsingCBT.h │ ├── LinkedList.h │ ├── Patient.h │ ├── Queries.h │ ├── TreeTypes.h │ ├── WorkerMenu.h │ ├── common_functions.h │ ├── common_types.h │ ├── parser.h │ └── stats.h ├── modules │ ├── BalancedTree.c │ ├── BinaryTree.c │ ├── Dates.c │ ├── HashTable.c │ ├── HeapUsingCBT.c │ ├── LinkedList.c │ ├── Patient.c │ └── common_functions.c └── worker │ ├── Makefile │ ├── WorkerMenu.c │ ├── parser.c │ ├── queries.c │ └── worker.c ├── DiseaseMonitor ├── Makefile ├── README.md ├── include │ ├── BalancedTree.h │ ├── BinaryTree.h │ ├── CompareFunctions.h │ ├── Dates.h │ ├── GlobalStructures.h │ ├── HashTable.h │ ├── HeapUsingCBT.h │ ├── Patient.h │ ├── Queries.h │ ├── TreeTypes.h │ └── common_types.h ├── input │ ├── large.txt │ └── small.txt ├── misc │ └── small.txt ├── modules │ ├── BalancedTree.c │ ├── BinaryTree.c │ ├── Dates.c │ ├── HashTable.c │ ├── HeapUsingCBT.c │ └── Patient.c └── programs │ ├── Queries.c │ ├── menu.c │ ├── monitor.c │ └── parser.c ├── DiseaseServer ├── create_programs.sh ├── include │ ├── BalancedTree.h │ ├── BinaryTree.h │ ├── CompareFunctions.h │ ├── Dates.h │ ├── GlobalStructures.h │ ├── HashTable.h │ ├── HeapUsingCBT.h │ ├── LinkedList.h │ ├── Patient.h │ ├── Queries.h │ ├── TreeTypes.h │ ├── WorkerMenu.h │ ├── buffer.h │ ├── common_functions.h │ ├── common_types.h │ └── stats.h ├── master │ ├── Makefile │ ├── master_main.c │ └── master_operation.c ├── misc │ ├── commands.txt │ ├── countries.txt │ ├── diseases.txt │ └── queries.txt ├── modules │ ├── BalancedTree.c │ ├── BinaryTree.c │ ├── Dates.c │ ├── HashTable.c │ ├── HeapUsingCBT.c │ ├── LinkedList.c │ ├── Patient.c │ └── common_functions.c ├── whoClient │ ├── Makefile │ ├── client_main.c │ └── client_operation.c ├── whoServer │ ├── Makefile │ ├── buffer.c │ ├── server_main.c │ ├── server_operation.c │ └── server_query_menu.c └── worker │ ├── Makefile │ ├── parser.c │ ├── worker_main.c │ ├── worker_menu.c │ └── worker_queries.c ├── LICENSE └── README.md /DiseaseAggregator/aggregator/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = diseaseAggregator 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build_aggregator 6 | SRC_DIRS = . ../modules 7 | INC_DIRS = ../include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name "*.c") 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ 17 | mv $(TARGET_EXEC) .. 18 | 19 | # c source 20 | $(BUILD_DIR)/%.c.o: %.c 21 | $(MKDIR_P) $(dir $@) 22 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 23 | 24 | 25 | .PHONY: clean 26 | 27 | clean: 28 | $(RM) -r $(BUILD_DIR) 29 | $(RM) 30 | 31 | run: 32 | ./$(TARGET_EXEC) -w 10 -b 100 -i input 33 | 34 | valgrind: 35 | valgrind ./$(TARGET_EXEC) 36 | 37 | 38 | -include $(DEPS) 39 | 40 | MKDIR_P ?= mkdir -p -------------------------------------------------------------------------------- /DiseaseAggregator/aggregator/aggregator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "common_types.h" 8 | #include "common_functions.h" 9 | #include "HashTable.h" 10 | #include "LinkedList.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "stats.h" 16 | 17 | // declaration of the menu function 18 | void menu(int* reading, int* writing, int n_workers, int* workers_ids, int buff_size, HashTable hash, char** names1, char** names2, char* input_dir); 19 | 20 | void aggregator(int n_workers, int buff_size, char* input_dir) { 21 | // hold a pointer for the subdirs that we are going to check 22 | struct dirent* sub_dir; 23 | // Open the input dir given by the user 24 | DIR* input = opendir(input_dir); 25 | // Check if any error occurs 26 | if (input == NULL) { 27 | fprintf(stderr, "Problem with the given imput directory. Exiting...\n"); 28 | exit(EXIT_FAILURE); 29 | } 30 | // Create n_workers child processes with fork, and then exec to redirect to another executable 31 | pid_t pid; 32 | // arrays to store the file descs coming from the pipes for reading and writing 33 | int reading[n_workers]; int writing[n_workers]; 34 | // array to store the pids of the childs 35 | pid_t workers_ids[n_workers]; 36 | mkdir("./tmp", S_IRWXU | S_IRWXG | S_IRWXO); 37 | char* names_1[n_workers]; 38 | char* names_2[n_workers]; 39 | for (int i = 0; i < n_workers; i++) { 40 | pid = fork(); 41 | // hold the name of the 2 named pipes 42 | char* str_i = itoa(i); 43 | names_1[i] = concat("./tmp/fifo_1_", str_i); 44 | names_2[i] = concat("./tmp/fifo_2_", str_i); 45 | free(str_i); 46 | if (pid > 0) { 47 | // the parent saves the child's pid 48 | workers_ids[i] = pid; 49 | // Create __two__ named pipes, so each process can write in one of them 50 | if (mkfifo(names_1[i], 0666) == -1) { 51 | if (errno != EEXIST) { 52 | perror("reciever: mkfifo"); 53 | exit(EXIT_FAILURE); 54 | } 55 | } 56 | 57 | if (mkfifo(names_2[i], 0666) == -1) { 58 | if (errno != EEXIST) { 59 | perror("reciever: mkfifo"); 60 | exit(EXIT_FAILURE); 61 | } 62 | } 63 | } else { 64 | // The child does the rest 65 | // call an exec function, in order to redirect the child in the worker file 66 | execl("./worker/worker", "worker", names_1[i], names_2[i], itoa(buff_size), input_dir, "init", NULL); 67 | // if we reach this point, then exec has returned, so sthg wrong has happened 68 | perror("execl"); 69 | exit(EXIT_FAILURE); 70 | } 71 | } 72 | // open the pipes and store the descriptors in the arrays that we have allocated 73 | for (int i = 0; i < n_workers; i++) { 74 | char* str_i = itoa(i); 75 | char* name1 = concat("./tmp/fifo_1_", str_i); 76 | char* name2 = concat("./tmp/fifo_2_", str_i); 77 | free(str_i); 78 | if ((reading[i] = open(name1, O_RDONLY, 0666)) == -1) { 79 | perror("creating"); 80 | exit(EXIT_FAILURE); 81 | } 82 | if ((writing[i] = open(name2, O_WRONLY, 0666)) == -1) { 83 | perror("creating"); 84 | exit(EXIT_FAILURE); 85 | } 86 | free(name1); 87 | free(name2); 88 | } 89 | int n_dirs = 0; 90 | // Check how many directories(aka countries) are in the parent dir 91 | while ((sub_dir = readdir(input)) != NULL) { 92 | n_dirs++; 93 | } 94 | rewinddir(input); 95 | // store the directories in an array 96 | // We aer gonna remove . and .. dirs 97 | n_dirs -= 2; 98 | char* dirs[n_dirs]; 99 | for (int i = 0; i < n_dirs; i++) { 100 | sub_dir = readdir(input); 101 | // Exclude the "." and ".." dirs 102 | if (strcmp(sub_dir->d_name, ".") && strcmp(sub_dir->d_name, "..")) 103 | dirs[i] = strdup(sub_dir->d_name); 104 | else 105 | i--; 106 | } 107 | // Create a hash table to store which dirs are held by which workers (key: pid, item: list of countries) 108 | HashTable dirs_to_workers = hash_create(n_workers, hash_strings, BUCKET_SIZE, NULL); 109 | // find out the correct way to split the dirs 110 | int split_no = n_dirs / n_workers; 111 | int remainder = n_dirs % n_workers; 112 | int count = 0; 113 | // assign in each worker `split_no` dirs 114 | for (int i = 0; i < n_workers; i++) { 115 | for (int j = 0; j < split_no; j++) { 116 | // assign the dir by informing the child process through the pipe 117 | write_to_pipe(writing[i], buff_size, dirs[count]); 118 | // save the tuple in the hash table 119 | hash_insert(dirs_to_workers, dirs[count], &workers_ids[i]); 120 | count++; 121 | } 122 | } 123 | //assign one dir to each worker, starting from worker 1 124 | int i = 0; 125 | // while there are countries remaining 126 | while (count < n_dirs) { 127 | // assign the dir by informing the child process through the pipe= 128 | write_to_pipe(writing[i], buff_size, dirs[count]); 129 | // save the tuple in the hash table 130 | hash_insert(dirs_to_workers, dirs[count], &workers_ids[i]); 131 | // adjust the pointers 132 | i++; count++; 133 | } 134 | // write this into every pipe so the workers know when the dirs that they are gonna parse end 135 | for (int i = 0; i < n_workers; i++) { 136 | write_to_pipe(writing[i], buff_size, "end"); 137 | } 138 | // update the nworkers variable in case that the dirs are less than the workers 139 | int active_workers = (split_no == 0) ? remainder : n_workers; 140 | // call the print function to prin the stats from the incoming files 141 | print_stats(active_workers, buff_size, reading); 142 | // close the input directory 143 | closedir(input); 144 | // call the menu to get queries from the user and pass them to the workers 145 | menu(reading, writing, active_workers, workers_ids, buff_size, dirs_to_workers, names_1, names_2, input_dir); 146 | //the menu returns when the user has requested to exit the program 147 | 148 | //free the hash table of the workers-countries 149 | hash_destroy(dirs_to_workers); 150 | // send a SIGKILL to the workers to end them 151 | for (int i = 0; i < n_workers; i++) { 152 | kill(workers_ids[i], SIGKILL); 153 | } 154 | // Delete all the pipes that we've opened 155 | for (int i = 0; i < n_workers; i++) { 156 | unlink(names_1[i]); 157 | unlink(names_2[i]); 158 | 159 | free(names_1[i]); 160 | free(names_2[i]); 161 | } 162 | // free stuff 163 | for (int i = 0; i < n_dirs; i++) { 164 | free(dirs[i]); 165 | } 166 | // wait until all the children are dead 167 | for (int i = 0; i < n_workers; i++) { 168 | wait(&workers_ids[i]); 169 | } 170 | // we are now ready to return to the main function to wrap it up 171 | } -------------------------------------------------------------------------------- /DiseaseAggregator/aggregator/main.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | 3 | void aggregator(int n_workers, int buff_size, char* input_dir); 4 | 5 | int main(int argc, char const *argv[]) { 6 | char* input_dir; 7 | int n_workers, buffer_size; 8 | if (argc < 7 || argc > 7) { 9 | printf("Use as ./diseaseAggregator –w numWorkers -b bufferSize -i input_dir\n"); 10 | exit(EXIT_FAILURE); 11 | } 12 | if (! strcmp(argv[1], "-w")) { 13 | n_workers = atoi(argv[2]); 14 | } else { 15 | printf("Use as ./diseaseAggregator –w numWorkers -b bufferSize -i input_dir\n"); 16 | exit(EXIT_FAILURE); 17 | } 18 | if (! strcmp(argv[3], "-b")) { 19 | buffer_size = atoi(argv[4]); 20 | } else { 21 | printf("Use as ./diseaseAggregator –w numWorkers -b bufferSize -i input_dir\n"); 22 | exit(EXIT_FAILURE); 23 | } 24 | if (! strcmp(argv[5], "-i")) { 25 | input_dir = strdup(argv[6]); 26 | } else { 27 | printf("Use as ./diseaseAggregator –w numWorkers -b bufferSize -i input_dir\n"); 28 | exit(EXIT_FAILURE); 29 | } 30 | aggregator(n_workers, buffer_size, input_dir); 31 | free(input_dir); 32 | exit(EXIT_SUCCESS); 33 | } -------------------------------------------------------------------------------- /DiseaseAggregator/aggregator/stats_parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "common_functions.h" 14 | 15 | void print_stats(int active_workers, int buff_size, int* reading) { 16 | // read from all the pipes 17 | fd_set active, read; 18 | // initialize the sets of the fds 19 | FD_ZERO(&active); 20 | for (int i = 0; i < active_workers; i++) { 21 | FD_SET(reading[i], &active); 22 | } 23 | int done = 0; 24 | while (done < active_workers) { 25 | // print the incoming stats from each worker 26 | // Block until an input arrives from one of the workers 27 | read = active; 28 | // find out how many workers are ready 29 | if (select(FD_SETSIZE, &read, NULL, NULL, NULL) < 0) { 30 | perror("select"); 31 | exit(EXIT_FAILURE); 32 | } 33 | // for all the workers that have already reported stats 34 | for (int i = 0; i < active_workers; i++) { 35 | if (FD_ISSET(reading[i], &read)) { 36 | // first thing: how many files the worker reported 37 | char* n = read_from_pipe(reading[i], buff_size); 38 | int n_files = atoi(n); 39 | for (int j = 0; j < n_files; j++) { 40 | // each file has a name, a country, and some diseases 41 | char* name = read_from_pipe(reading[i], buff_size); 42 | char* country = read_from_pipe(reading[i], buff_size); 43 | char* n_dis = read_from_pipe(reading[i], buff_size); 44 | int n_diseases = atoi(n_dis); 45 | fprintf(stdout, "%s\n%s\n", name, country); 46 | // for each disease 47 | for (int k = 0; k < n_diseases; k++) { 48 | // parse the stats 49 | char* disease = read_from_pipe(reading[i], buff_size); 50 | fprintf(stdout, "%s\n", disease); 51 | free(disease); 52 | char* info = read_from_pipe(reading[i], buff_size); 53 | fprintf(stdout, "%s\n", info); 54 | free(info); 55 | } 56 | fprintf(stdout, "\n"); 57 | // no leaks! 58 | free(name); 59 | free(country); 60 | free(n_dis); 61 | } 62 | free(n); 63 | done++; 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /DiseaseAggregator/create_executable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd worker 4 | make 5 | cd ../aggregator 6 | make 7 | cd .. 8 | -------------------------------------------------------------------------------- /DiseaseAggregator/create_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Error messages for later use 4 | USAGE="Usage: ./create_infiles.sh diseasesFile countriesFile input_dir numFilesPerDirectory numRecordsPerFile" 5 | ARG_ERROR="Problem with the args provided" 6 | rm -rf input/ 7 | # Check if exactly 5 args are provided 8 | if [ $# -ne 5 ]; then 9 | echo ${USAGE} ; 10 | exit 1 11 | fi 12 | 13 | # Keep all the arguments 14 | DISEASES_FILE=$1 15 | COUNTRIES_FILE=$2 16 | INPUT_DIR=$3 17 | NUM_FILES_PER_DIR=$4 18 | NUM_RECS_PER_FILE=$5 19 | 20 | # Check if the disease and country files (mandatory) exists, and if the two numbers given are grater than 0. 21 | if [ ! -e ${DISEASES_FILE} -o ! -e ${COUNTRIES_FILE} -o ${NUM_FILES_PER_DIR} -lt "0" -o ${NUM_RECS_PER_FILE} -lt "0" ] 22 | then echo ${ARG_ERROR}; 23 | exit 1 24 | fi 25 | 26 | # Create 4 arrays containing all the diseases and all the countries available, as well as the names 27 | 28 | names=(Michael Christopher Jessica Matthew Ashley Jennifer Joshua Amanda Daniel David James Robert John Joseph Andrew Ryan Brandon Jason Justin Sarah William Jonathan Stephanie Brian Nicole Nicholas 29 | Anthony Heather Eric Elizabeth Adam Megan Melissa Kevin Steven Thomas Timothy Christina Kyle Rachel Laura Lauren Amber Brittany Danielle Richard Kimberly Jeffrey Amy Crystal Michelle Tiffany Jeremy Benjamin Mark Emily Aaron Charles Rebecca Jacob Stephen Patrick Sean Erin Zachary Jamie Kelly Samantha Nathan Sara Dustin Paul Angela Tyler Scott 30 | Katherine Andrea Gregory Erica Mary Travis Lisa Kenneth Bryan Lindsey Kristen Jose Alexander Jesse KatieLindsay Shannon Vanessa Courtne Christine Alicia Cody Allison Bradley Samuel Shawn April Derek Kathryn Kristin Chad Jenna Tara Maria Krystal Jared Anna Edward Julie Peter Holly Marcus Kristina Natalie Jordan) 31 | 32 | surnames=(SMITH JOHNSON WILLIAMS BROWN JONES MILLER DAVIS GARCIA RODRIGUEZ WILSON MARTINEZ ANDERSON TAYLOR THOMAS HERNANDEZ MOORE MARTIN JACKSON THOMPSON WHITE LOPEZ LEE GONZALEZ HARRIS CLARK LEWIS ROBINSON WALKER PEREZ HALL YOUNG ALLEN SANCHEZ WRIGHT KING SCOTT GREEN BAKER 33 | ADAMS NELSON HILL RAMIREZ CAMPBELL MITCHELL ROBERTS CARTE PHILLIPS EVANS TURNER TORRES PARKER COLLINS EDWARDS STEWART FLORES MORRIS NGUYEN MURPHY RIVERA COOK ROGERS MORGAN PETERSON COOPER REE BAILEY BELL 34 | GOMEZ KELLY HOWARD WARD COX DIAZ RICHARDSON WOOD WATSON BROOKS BENNETT GRAY JAMES REYES CRUZ HUGHES PRICE MYERS LONG FOSTER SANDERS ROSS MORALES POWELL SULLIVAN RUSSELL ORTIZ JENKINS GUTIERREZ PERRY BUTLER BARNES FISHER HENDERSON COLEMAN SIMMONS PATTERSON JORDAN REYNOLDS HAMILTON GRAHAMKIM GONZALES ALEXANDER RAMOS WALLACE GRIFFIN WEST COLE HAYES CHAVEZ ) 35 | 36 | diseases+=($(cat ${DISEASES_FILE})) 37 | countries+=($(cat ${COUNTRIES_FILE})) 38 | 39 | # Create the input dir if it is not already there 40 | if [ ! -e ${INPUT_DIR} ] 41 | then mkdir ${INPUT_DIR}; 42 | fi 43 | 44 | # Get the length of the arrays so we can iterate them 45 | num_countries=${#countries[@]} 46 | num_diseases=${#diseases[@]} 47 | num_names=${#names[@]} 48 | num_surnames=${#surnames[@]} 49 | 50 | # go to the input dir 51 | cd $INPUT_DIR 52 | 53 | # For each country 54 | for ((i = 0; i < num_countries; i++)); do 55 | # Create the apropriate subdir 56 | mkdir ${countries[i]} 57 | # get into there 58 | cd ${countries[i]} 59 | # For the number of files that we want to create 60 | for ((j = 0; j < $NUM_FILES_PER_DIR; j++)); do 61 | # Randomize the date 62 | dd=$((1 + RANDOM % 31)) 63 | mm=$((1 + RANDOM % 13)) 64 | yyyy=$((2018 + RANDOM % 3)) 65 | # Check that both day and month are 2 digits, else fix them 66 | if [ $dd -lt "10" ] 67 | then dd="0${dd}"; 68 | fi 69 | if [ $mm -lt "10" ] 70 | then mm="0${mm}"; 71 | fi 72 | file_name=$dd-$mm-$yyyy 73 | if [ ! -e $file_name ]; then 74 | touch $file_name 75 | for ((k = 0; k < $NUM_RECS_PER_FILE; k++)); do 76 | action=$((RANDOM % 100)) 77 | id=$((1 + RANDOM % (2 * $NUM_RECS_PER_FILE * $NUM_FILES_PER_DIR))) 78 | age=$((1 + RANDOM % 120)) 79 | a=$((RANDOM % $num_diseases)) 80 | b=$((RANDOM % $num_names)) 81 | c=$((RANDOM % $num_surnames)) 82 | name=${names[b]} 83 | surname=${surnames[c]} 84 | disease=${diseases[a]} 85 | person="$name $surname $disease $age" 86 | if [ ${action} -lt "30" ]; then 87 | echo $id EXIT $person >> $file_name 88 | else 89 | echo $id ENTER $person >> $file_name 90 | 91 | fi 92 | done 93 | fi 94 | done 95 | cd ../ 96 | done -------------------------------------------------------------------------------- /DiseaseAggregator/include/BalancedTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BinaryTree.h" 3 | #include "Dates.h" 4 | 5 | 6 | // Our entries to our bst will be of this form 7 | struct tree_entry { 8 | Date date; 9 | Pointer assigned_patient; 10 | }; 11 | typedef struct tree_entry* TreeEntry; 12 | 13 | typedef BinaryTree BalancedTree; 14 | typedef TreeEntry BalancedTreeEntry; 15 | 16 | BalancedTreeEntry create_balanced_tree_entry(Date date, Pointer assgn); 17 | TreeNode create_tree_node(BalancedTreeEntry value); 18 | BalancedTree create_balanced_tree(CompareFunc compare, DestroyFunc destroy); 19 | void balanced_tree_insert(BalancedTree tree, BalancedTreeEntry value); 20 | int total_nodes_grater_than(BalancedTree tree, Pointer x, ConditionFunc cond, char* cond_item); 21 | int grater_than(TreeNode node, Pointer x, CompareFunc compare, ConditionFunc cond, char* cond_item); 22 | int balanced_tree_cond_traverse(Pointer tree, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2); 23 | int node_cond_traverse(TreeNode node, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2); 24 | void balanced_tree_traverse(BalancedTree tree, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p); 25 | void node_traverse(TreeNode node, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p); 26 | void balanced_tree_destroy(Pointer tree); 27 | void destroy_node(TreeNode node, DestroyFunc destroy); 28 | -------------------------------------------------------------------------------- /DiseaseAggregator/include/BinaryTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TreeTypes.h" 4 | 5 | typedef struct bs_tree_node* TreeNode; 6 | 7 | struct bs_tree_node { 8 | TreeNode left, right; 9 | Pointer value; 10 | uint height; 11 | }; 12 | 13 | typedef Tree BinaryTree; 14 | 15 | TreeNode create_binary_node(Pointer item); 16 | TreeNode insert_binary_node(TreeNode node, CompareFunc compare, Pointer item); 17 | BinaryTree create_binary_tree(CompareFunc compare, DestroyFunc destroy); 18 | void binary_tree_insert(BinaryTree tree, Pointer item); 19 | void destroy_node(TreeNode node, DestroyFunc destroy); -------------------------------------------------------------------------------- /DiseaseAggregator/include/CompareFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | #include "BalancedTree.h" 4 | #include "Dates.h" 5 | 6 | int compare(Pointer first, Pointer second) { 7 | TreeEntry entry1 = (TreeEntry)first; 8 | TreeEntry entry2 = (TreeEntry)second; 9 | return compare_dates(entry1->date, entry2->date); 10 | } -------------------------------------------------------------------------------- /DiseaseAggregator/include/Dates.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | typedef struct date { 6 | int day; 7 | int month; 8 | int year; 9 | } Date; 10 | 11 | #define EOF_DATE (Date)0 12 | 13 | bool empty_string(char* str); 14 | Date string_to_date(char* d); 15 | char* date_to_string(Date date); 16 | int compare_dates(Date date1, Date date2); 17 | bool check_valid_dates(Date date1, Date date2); 18 | bool check_if_null_date(Date date); 19 | bool check_equal_dates(Date date1, Date date2); -------------------------------------------------------------------------------- /DiseaseAggregator/include/GlobalStructures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Includes that must be made 4 | #include "common_types.h" 5 | #include "Patient.h" 6 | #include "Dates.h" 7 | #include "HashTable.h" 8 | #include "BalancedTree.h" 9 | #include "HeapUsingCBT.h" 10 | #include "Queries.h" 11 | 12 | // Our global pointers to stroe some sizes 13 | int num_diseases, num_countries; 14 | 15 | // Our global pointers to store the hash tables 16 | HashTable diseaseHashTable; 17 | HashTable countryHashTable; 18 | HashTable patients; 19 | 20 | // number of lines in the file opened 21 | int lines; -------------------------------------------------------------------------------- /DiseaseAggregator/include/HashTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | 4 | typedef uint (*HashFunc)(Pointer); 5 | 6 | struct hash_entry { 7 | char* key; 8 | Pointer item; 9 | }; 10 | 11 | typedef struct hash_entry* HashEntry; 12 | 13 | typedef struct hash_node* HashNode; 14 | 15 | struct hash_node { 16 | HashEntry* bucket; 17 | HashNode next; 18 | }; 19 | 20 | 21 | 22 | #define HASH_EOF (HashNode)0 23 | 24 | struct hash_table { 25 | HashNode* array; 26 | int size; 27 | int items; 28 | int bucket_size; 29 | int bucket_max_entries; 30 | HashFunc hash_function; 31 | DestroyFunc destroy_items; 32 | }; 33 | 34 | typedef struct hash_table* HashTable; 35 | 36 | HashEntry create_hash_entry(char* key, Pointer item); 37 | HashTable hash_create(int size, HashFunc hash_fn, int bucket_size, DestroyFunc destroy); 38 | void hash_insert(HashTable ht, char* key, Pointer item); 39 | HashEntry hash_search(HashTable ht, char* name); 40 | void hash_update(HashTable ht, char* key, Pointer new_item); 41 | void hash_traverse(HashTable ht, VisitFunc print, Pointer d1, Pointer d2, Pointer p); 42 | void hash_destroy(HashTable ht); -------------------------------------------------------------------------------- /DiseaseAggregator/include/HeapUsingCBT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TreeTypes.h" 4 | 5 | struct binary_tree_entry { 6 | char* key; 7 | int priority; 8 | }; 9 | 10 | typedef struct binary_tree_entry* BTEntry; 11 | typedef struct binary_tree_node* BTNode; 12 | 13 | struct binary_tree_node { 14 | BTNode left, right, parent; 15 | BTEntry value; 16 | }; 17 | // A Complete binary tree node, is just a node 18 | typedef BTNode CBTreeNode; 19 | 20 | // As well as a complete binary tree 21 | typedef Tree CBTree; 22 | 23 | CBTree create_CBTree(DestroyFunc destroy); 24 | CBTreeNode create_cbinary_node(Pointer item); 25 | CBTreeNode get_node_by_index (CBTree tree, int i); 26 | void CBTree_insert(CBTree tree, BTEntry entry); 27 | // void CBTree_destroy(Pointer t); 28 | 29 | // Our heap is just a complete binary tree 30 | typedef CBTree Heap; 31 | 32 | typedef BTEntry HeapEntry; 33 | typedef CBTreeNode HeapNode; 34 | 35 | HeapEntry create_heap_entry(char* key, int priority); 36 | Heap create_heap(DestroyFunc destroy); 37 | void heapify_up(Heap heap, BTNode node); 38 | void heapify_down(Heap heap, BTNode node); 39 | void heap_insert(Heap heap, int priority, char* key); 40 | HeapNode get_nth_node(Heap heap, int i); 41 | HeapEntry pop(Heap heap); 42 | void destroy_heap(Heap heap); -------------------------------------------------------------------------------- /DiseaseAggregator/include/LinkedList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | typedef struct list_node* ListNode; 6 | typedef struct list* List; 7 | 8 | 9 | struct list_node { 10 | Pointer value; 11 | ListNode next; 12 | }; 13 | 14 | struct list { 15 | ListNode head; 16 | int size; 17 | DestroyFunc destroy; 18 | CompareFunc compare; 19 | }; 20 | 21 | 22 | List create_list(CompareFunc compare, DestroyFunc destroy); 23 | void list_insert(List list, Pointer node); 24 | bool list_search(List list, Pointer value); 25 | bool in_list(List list, Pointer node_info); 26 | int list_size(List list); 27 | bool is_empty(List list); 28 | Pointer list_nth(List list, int n); 29 | void destroy_list(Pointer list); 30 | -------------------------------------------------------------------------------- /DiseaseAggregator/include/Patient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | #include "Dates.h" 4 | 5 | typedef struct patient { 6 | char* id; 7 | char* first_name; 8 | char* last_name; 9 | char* disease; 10 | char* country; 11 | int age; 12 | Date entry_date; 13 | Date exit_date; 14 | } Patient; 15 | 16 | Patient* create_patient(char* str, char* country, char* entry_date); 17 | void destroy_patient(Pointer p); -------------------------------------------------------------------------------- /DiseaseAggregator/include/Queries.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | #include "common_functions.h" 5 | #include "HashTable.h" 6 | #include "BalancedTree.h" 7 | #include "HashTable.h" 8 | #include "Dates.h" 9 | #include "Patient.h" 10 | #include "HeapUsingCBT.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | bool recordPatientExit(char* info, HashTable patients, char* exit_d); 17 | int disease_frequency(char* virus, char* arg2, char* arg3, char* country, HashTable diseases_hash); 18 | char* search_patient_record(char* r_id, HashTable patients); 19 | char* num_patient_admissions(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash); 20 | char* num_patient_discharges(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash); 21 | void topk_age_ranges(int k, char* country, char* disease, char* day1, char* day2, HashTable diseases_hash, int write, int buff_size); 22 | -------------------------------------------------------------------------------- /DiseaseAggregator/include/TreeTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | // Our nodes have 2 child nodes, their value, and ther height in the tree 6 | // in order for it to remain balanced 7 | 8 | struct tree { 9 | Pointer root; 10 | int size; 11 | CompareFunc compare; 12 | DestroyFunc destroy; 13 | }; 14 | 15 | typedef struct tree* Tree; -------------------------------------------------------------------------------- /DiseaseAggregator/include/WorkerMenu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "HashTable.h" 4 | #include "LinkedList.h" 5 | #include "Queries.h" 6 | 7 | bool worker_menu(char* query, List dirs, HashTable Patients,HashTable diseases_hash, int writing, int buff_size); -------------------------------------------------------------------------------- /DiseaseAggregator/include/common_functions.h: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "BalancedTree.h" 3 | #include "HashTable.h" 4 | #include "LinkedList.h" 5 | 6 | int compare_strings (Pointer a, Pointer b); 7 | int compare(Pointer first, Pointer second); 8 | uint hash_strings(void* key); 9 | int nlines(FILE* input); 10 | int n_words(char* str); 11 | char* nth_word(char* s, int n); 12 | char* read_from_pipe(int fd, int buff_size); 13 | char* concat(char* str1, char* str2); 14 | char* itoa(int n); 15 | void write_to_pipe(int fd, int buff_size, char* info); 16 | void print_list_contents(Pointer ent, Pointer d1, Pointer d2, Pointer d3, Pointer d4); 17 | void print_countries(Pointer ent, Pointer d1, Pointer d2, Pointer d3, Pointer d4); 18 | int get_pos_from_pid(int pid, int* workers, int n_workers); 19 | int n_files_in_worker(char* path, List countries); -------------------------------------------------------------------------------- /DiseaseAggregator/include/common_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define STRING_SIZE 1024 11 | #define HASH_SIZE 100 12 | #define BUCKET_SIZE 100 13 | #define FAILED -1 14 | #define PERMS 0777 15 | // Usefull typedefs 16 | typedef void* Pointer; 17 | 18 | typedef unsigned int uint; 19 | 20 | typedef int (*CompareFunc)(Pointer a, Pointer b); 21 | typedef void (*DestroyFunc)(Pointer value); 22 | typedef uint (*HashFunc)(Pointer); 23 | typedef void (*VisitFunc)(Pointer, Pointer, Pointer, Pointer, Pointer); 24 | typedef bool (*ConditionFunc)(Pointer, Pointer, Pointer, Pointer); 25 | 26 | 27 | void parse_input (char* file, int bucket_size); 28 | void monitor_menu(); -------------------------------------------------------------------------------- /DiseaseAggregator/include/parser.h: -------------------------------------------------------------------------------- 1 | void parser(char* input_dir, int buff_size, List dirs, List parsed_files, int writing, HashTable patients, HashTable diseases_hash, int* success, int* failed, bool send_stats, bool from_signal); -------------------------------------------------------------------------------- /DiseaseAggregator/include/stats.h: -------------------------------------------------------------------------------- 1 | void print_stats(int active_workers, int buff_size, int* reading); -------------------------------------------------------------------------------- /DiseaseAggregator/modules/BalancedTree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "BalancedTree.h" 3 | #include "Patient.h" 4 | #include 5 | #include 6 | 7 | extern Pointer empty; 8 | 9 | // Create a new tree entry 10 | BalancedTreeEntry create_balanced_tree_entry(Date date, Pointer assgn) { 11 | BalancedTreeEntry entry = malloc(sizeof(*entry)); 12 | assert(entry != NULL); 13 | entry->date = date; 14 | entry->assigned_patient = assgn; 15 | 16 | return entry; 17 | } 18 | 19 | // Create a new tree node 20 | TreeNode create_tree_node(BalancedTreeEntry value) { 21 | return create_binary_node(value); 22 | } 23 | 24 | // Create an empty balanced tree 25 | BalancedTree create_balanced_tree(CompareFunc compare, DestroyFunc destroy) { 26 | return create_binary_tree(compare, destroy); 27 | } 28 | 29 | // insert a node to the dree 30 | TreeNode insert_node_to_tree(Tree tree, TreeNode node, BalancedTreeEntry entry) { 31 | return insert_binary_node(node, tree->compare, entry); 32 | } 33 | 34 | // Give the root of the tree, in order to insert a new entry to it. 35 | void balanced_tree_insert(BalancedTree tree, BalancedTreeEntry value) { 36 | assert(value != NULL); 37 | tree->root = insert_node_to_tree(tree, tree->root, value); 38 | tree->size++; 39 | } 40 | 41 | // Find all the nodes in a tree that are grater than a const, and satisfy a given condition 42 | int total_nodes_grater_than(BalancedTree tree, Pointer x, ConditionFunc cond, char* cond_item) { 43 | return grater_than(tree->root, x, tree->compare, cond, cond_item); 44 | } 45 | 46 | // Recursive function to find all the nodes in a tree that are grater than a const 47 | int grater_than(TreeNode node, Pointer x, CompareFunc compare, ConditionFunc cond, char* cond_item) { 48 | int count = 0; 49 | if (node != NULL) { 50 | if (compare(x, node->value) <= 0) { 51 | count += grater_than(node->left, x, compare, cond, cond_item); 52 | if (cond == NULL) 53 | count++; 54 | else { 55 | if (cond(node->value, cond_item, NULL, NULL) == true) 56 | count++; 57 | } 58 | } 59 | count += grater_than(node->right, x, compare, cond, cond_item); 60 | } 61 | return count; 62 | } 63 | 64 | // Traverse a balanced tree, bt recursively calling node_traverse 65 | int balanced_tree_cond_traverse(Pointer t, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2) { 66 | BalancedTree tree = (BalancedTree)t; 67 | return node_cond_traverse(tree->root, cond, a, d1, d2); 68 | } 69 | 70 | // Return how many nodes satisfy the condition fuction passed to the func. 71 | int node_cond_traverse(TreeNode node, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2) { 72 | int count = 0; 73 | if (node != NULL) { 74 | count += node_cond_traverse(node->left, cond, a, d1, d2); 75 | if (cond(node->value, a, d1, d2) == true) { 76 | count++; 77 | } 78 | count += node_cond_traverse(node->right, cond, a, d1, d2); 79 | } 80 | return count; 81 | } 82 | 83 | // Traverse a balanced tree with a visit function 84 | void balanced_tree_traverse(BalancedTree tree, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p) { 85 | node_traverse(tree->root, visit, c, d1, d2, p); 86 | } 87 | 88 | void node_traverse(TreeNode node, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p) { 89 | if (node != NULL) { 90 | node_traverse(node->left, visit, c, d1, d2, p); 91 | visit(node->value, c, d1, d2, p); 92 | node_traverse(node->right, visit, c, d1, d2, p); 93 | } 94 | } 95 | 96 | // Destroy a balanced tree, by freeing all the memory allocated 97 | void balanced_tree_destroy(Pointer t) { 98 | BalancedTree tree = (BalancedTree)t; 99 | if (tree != empty) { 100 | destroy_node(tree->root, tree->destroy); 101 | free(tree); 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /DiseaseAggregator/modules/BinaryTree.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "BinaryTree.h" 4 | extern Pointer empty; 5 | // Create a node for a binary tree 6 | TreeNode create_binary_node(Pointer item) { 7 | TreeNode node = malloc(sizeof(*node)); 8 | assert(node != NULL); 9 | node->value = item; 10 | node->left = node->right = NULL; 11 | 12 | node->height = 0; // In this implementation we do not care about the height 13 | 14 | return node; 15 | } 16 | 17 | // Insert a node into a binary tree, specialized in __binary search__ 18 | TreeNode insert_binary_node(TreeNode node, CompareFunc compare, Pointer item) { 19 | if (node == NULL) { 20 | // Base case, if we reach a leaf, insert the entry 21 | return create_binary_node(item); 22 | } 23 | // Apply the rules of binary search 24 | if (compare(item, node->value) <= 0) { 25 | node->left = insert_binary_node(node->left, compare, item); 26 | } else { 27 | node->right = insert_binary_node(node->right, compare, item); 28 | } 29 | return node; 30 | } 31 | 32 | // Initialize the bt struct 33 | BinaryTree create_binary_tree(CompareFunc compare, DestroyFunc destroy) { 34 | BinaryTree tree = malloc(sizeof(*tree)); 35 | assert(tree != NULL); 36 | tree->root = NULL; 37 | tree->compare = compare; 38 | tree->destroy = destroy; 39 | tree->size = 0; 40 | 41 | return tree; 42 | } 43 | 44 | // Insert to the binary tree, by recursively calling the insert function, starting from the tree's root 45 | void binary_tree_insert(BinaryTree tree, Pointer item) { 46 | tree->root = insert_binary_node(tree->root, tree->compare, item); 47 | tree->size++; 48 | } 49 | 50 | // Destroy a binary tree, by applying a destroy function in ecah node 51 | void destroy_node(TreeNode node, DestroyFunc destroy) { 52 | if (node != NULL) { 53 | destroy_node(node->left, destroy); 54 | destroy_node(node->right, destroy); 55 | if (destroy != NULL) 56 | destroy(node->value); 57 | free(node->value); 58 | free(node); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DiseaseAggregator/modules/Dates.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Dates.h" 6 | 7 | bool empty_string(char* str) { 8 | if (str == NULL) 9 | return true; 10 | for (int i = 0;; i++) { 11 | if (str[i] != '\n' && str[i] != ' ' && str[i] != '\0' && str[i] != '-') 12 | return false; 13 | if (str[i] == '\0' || str[i] == '\0') 14 | return true; 15 | } 16 | } 17 | 18 | // Convert a DD-MM-YYYY string to an actual date 19 | Date string_to_date(char* d) { 20 | // We know that our string type is __identical__ to DD-MM-YYYY. 21 | // so, we are going to take advantage of it 22 | if (empty_string(d)) { 23 | Date new_date = {.day = 0, .month = 0, .year = 0}; 24 | return new_date; 25 | } 26 | char delim[3] = "-\n"; 27 | char* day = strtok(d, delim); 28 | char* month = strtok(NULL, delim); 29 | char* year = strtok(NULL, delim); 30 | assert(day != NULL && month != NULL && year!= NULL); 31 | // Update the struct fields and return it 32 | Date new_date = {.day = atoi(day), .month = atoi(month), .year = atoi(year)}; 33 | return new_date; 34 | } 35 | 36 | char* date_to_string(Date date) { 37 | if (check_if_null_date(date)) { 38 | return "--"; 39 | } 40 | // take advantage of the date format 41 | char* res = malloc(11 * sizeof(*res)); 42 | snprintf(res, 11, "%d-%d-%d", date.day, date.month, date.year); 43 | return res; 44 | } 45 | 46 | // Returns true if date is null (aka -) 47 | bool check_if_null_date(Date date) { 48 | return date.day == 0 ? true : false; 49 | } 50 | 51 | // Checks if a pair of dates is valid, meaning the first is before the second 52 | bool check_valid_dates(Date date1, Date date2) { 53 | return (compare_dates(date1, date2) < 0) ? true : false; 54 | } 55 | 56 | // Comparing 2 dates. Return -1 if 1st is prior(or euqal) to 2nd, or 1 otherwise 57 | int compare_dates(Date date1, Date date2) { 58 | if (date1.year < date2.year) 59 | return -1; 60 | else if (date1.year > date2.year) 61 | return 1; 62 | else { 63 | if (date1.month < date2.month) 64 | return -1; 65 | else if (date1.month > date2.month) 66 | return 1; 67 | else { 68 | if (date1.day <= date2.day) 69 | return -1; 70 | else 71 | return 1; 72 | } 73 | } 74 | } 75 | 76 | bool check_equal_dates(Date date1, Date date2) { 77 | return (date1.day == date2.day && date1.month == date2.month 78 | && date1.year == date2.year); 79 | } -------------------------------------------------------------------------------- /DiseaseAggregator/modules/HashTable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "HashTable.h" 4 | #include 5 | 6 | // Special pointer to indicate an empty entry 7 | Pointer empty = ∅ 8 | 9 | HashEntry create_hash_entry(char* key, Pointer item) { 10 | HashEntry new_entry = malloc(sizeof(*new_entry)); 11 | assert(new_entry != NULL); 12 | new_entry->key = key; 13 | new_entry->item = item; 14 | 15 | return new_entry; 16 | } 17 | 18 | // Create a node for the hash table, given its bucket size 19 | HashNode create_hash_node(int n_buckets) { 20 | HashNode new_node = malloc(sizeof(*new_node)); 21 | assert(new_node != NULL); 22 | new_node->bucket = malloc(n_buckets * sizeof(HashEntry)); 23 | // initialize our entries to null 24 | for (int j = 0; j < n_buckets; j++) { 25 | new_node->bucket[j] = create_hash_entry(empty, empty); 26 | } 27 | // We are going to use that as an overflow list pointer for each bucket 28 | new_node->next = NULL; 29 | 30 | return new_node; 31 | } 32 | 33 | // Create an empty hash table, with pre-determined size 34 | HashTable hash_create(int size, HashFunc hash_fn, int bucket_size, DestroyFunc destroy) { 35 | HashTable ht = malloc(sizeof(*ht)); 36 | assert(ht != NULL); 37 | assert(size >= 0); 38 | // Hold the bucketsize, so that is a multiple of the size of the struct. 39 | ht->bucket_size = bucket_size; 40 | ht->bucket_max_entries = (bucket_size - sizeof(Pointer)) / sizeof(struct hash_entry); 41 | if (bucket_size / sizeof(struct hash_entry) < 1) { 42 | printf("Fatal error, bucketsize too small. Exiting the monitor\n"); 43 | exit(EXIT_FAILURE); 44 | } 45 | // allocate space for our array 46 | ht->array = malloc(size * sizeof(HashNode)); 47 | // create the first nodes of each bucket's overflow list 48 | for (int i = 0; i < size; i++) { 49 | ht->array[i] = create_hash_node(ht->bucket_max_entries); 50 | } 51 | // Store the size of the ht 52 | ht->size = size; 53 | // Initially the ht is empty 54 | ht->items = 0; 55 | // We deferentiate the hts, by passing each hash & destroy function into the struct 56 | ht->hash_function = hash_fn; 57 | ht->destroy_items = destroy; 58 | return ht; 59 | } 60 | 61 | // Insert a new entry in the hash table 62 | void hash_insert(HashTable ht, char* key, Pointer item) { 63 | // Use the hash function to determine where our new entry is gonna go 64 | int hash_id = ht->hash_function(key) % ht->size; 65 | int pos; 66 | bool found_empty = false; 67 | HashNode requested = ht->array[hash_id]; 68 | // forever running loop until we find an empty space 69 | while (true) { 70 | for (pos = 0; pos < ht->bucket_max_entries; pos++) { 71 | // if we reach an empty entry, break our loop 72 | if (requested->bucket[pos]->item == empty) { 73 | found_empty = true; 74 | break; 75 | } 76 | } 77 | if (found_empty) 78 | break; 79 | // if we do not have an other element in our overflow list 80 | if (requested->next == NULL) { 81 | // create a new one 82 | HashNode new_node = create_hash_node(ht->bucket_max_entries); 83 | // and assign it to our node 84 | requested->next = new_node; 85 | requested = new_node; 86 | } else { 87 | // else, just proceed to the next member of the overflow list 88 | requested = requested->next; 89 | } 90 | } 91 | // Insert the new entry in the correct position 92 | requested->bucket[pos]->item = item; 93 | requested->bucket[pos]->key = key; 94 | // increase the ht items by one 95 | ht->items++; 96 | } 97 | // Search for a specific entry in the ht, given the key. If search fails, null is returned 98 | HashEntry hash_search(HashTable ht, char* key) { 99 | // Use the hash function to determine where our new entry is gonna go 100 | int hash_id = ht->hash_function(key) % ht->size; 101 | HashNode requested = ht->array[hash_id]; 102 | // run the loop until there are no more elements in the bucket's overflow list 103 | while (requested != NULL) { 104 | // traverse all the entries in the hash node, trying to find the desired one 105 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 106 | // if the entry has something in it, proceed, else we did not find what we wanted 107 | if (requested->bucket[pos]->item != empty) { 108 | if (strcmp(requested->bucket[pos]->key, key) == 0) 109 | return requested->bucket[pos]; 110 | } else { 111 | return NULL; 112 | } 113 | } 114 | // proceed to the next member of the overflow list 115 | requested = requested->next; 116 | } 117 | // the entry was not found, so return null 118 | return NULL; 119 | } 120 | 121 | // Update a hash entry providing a new item 122 | void hash_update(HashTable ht, char* key, Pointer new_item) { 123 | HashEntry entry = hash_search(ht, key); 124 | if (entry) { 125 | entry->item = new_item; //TODO: Maybe free the old one 126 | } else { 127 | hash_insert(ht, key, new_item); 128 | } 129 | } 130 | // Traverse the ht, by applying a visit function to each entry 131 | void hash_traverse(HashTable ht, VisitFunc visit, Pointer d1, Pointer d2, Pointer p) { 132 | // traverse all the buckets in our hash table 133 | for (int i = 0; i < ht->size; i++) { 134 | HashNode current = ht->array[i]; 135 | // run the loop until there are no more elements in the bucket's overflow list 136 | while (current != NULL) { 137 | // traverse all the entries in the hash node 138 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 139 | // if the item is not empty, apply the visit function 140 | if (current->bucket[pos]->item != empty) { 141 | char* str = current->bucket[pos]->key; 142 | visit(current->bucket[pos], d1, d2, str, p); 143 | } 144 | } 145 | // proceed to the next member of the overflow list 146 | current = current->next; 147 | } 148 | } 149 | } 150 | 151 | // Free the memory allocated by the hash table by destroying the ht 152 | void hash_destroy(HashTable ht) { 153 | // we want to free all the buckets 154 | for (int i = 0; i < ht->size; i++) { 155 | HashNode current = ht->array[i]; 156 | // traverse the overflow list 157 | while (current != NULL) { 158 | HashNode next_node = current->next; 159 | // destroy all the entries in the node 160 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 161 | if (current->bucket[pos]->item != empty && ht->destroy_items != NULL) 162 | ht->destroy_items(current->bucket[pos]->item); 163 | free(current->bucket[pos]); 164 | } 165 | // free the pointers to the bucket, and the bucket itself 166 | free(current->bucket); 167 | free(current); 168 | // proceed to the next member of the overflow list 169 | current = next_node; 170 | } 171 | } 172 | // free the array, and the hash table it self 173 | free(ht->array); 174 | free(ht); 175 | } -------------------------------------------------------------------------------- /DiseaseAggregator/modules/HeapUsingCBT.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "HeapUsingCBT.h" 3 | #include 4 | #include 5 | 6 | 7 | //============= Complete binary tree functions ================// 8 | 9 | // We are going to use those function in order to create a heap 10 | 11 | CBTree create_CBTree(DestroyFunc destroy) { 12 | CBTree tree = malloc(sizeof(*tree)); 13 | tree->root = NULL; 14 | tree->compare = NULL; 15 | tree->destroy = destroy; 16 | tree->size = 0; 17 | 18 | return tree; 19 | } 20 | 21 | CBTreeNode create_cbinary_node(Pointer item) { 22 | CBTreeNode node = malloc(sizeof(*node)); 23 | node->value = item; 24 | node->left = node->right = node->parent = NULL; 25 | 26 | return node; 27 | } 28 | 29 | // Given a integer, return the node in the correct position of that tree 30 | CBTreeNode get_node_by_index (CBTree tree, int i) { 31 | // basecase: if we want the 1st item, then it's in the root 32 | if (i == 1){ 33 | return tree->root; 34 | } 35 | // else, we want to call recursively the function to reach the childs 36 | // all the even numbers are right childs 37 | else if (i % 2 == 0) { 38 | CBTreeNode node = get_node_by_index(tree, i / 2); 39 | return node->left; 40 | } 41 | // all the odd numbers are right childs 42 | else { 43 | CBTreeNode node = get_node_by_index(tree, i / 2); 44 | return node->right; 45 | } 46 | } 47 | 48 | // Insert a new entry into a complete binary tree 49 | void CBTree_insert(CBTree tree, BTEntry entry) { 50 | int pos = tree->size + 1; 51 | // Same logic with the previous function 52 | if (pos == 1) { 53 | tree->root = create_cbinary_node(entry); 54 | } 55 | else if (pos % 2 == 0) { 56 | CBTreeNode node = get_node_by_index(tree, pos / 2); 57 | node->left = create_cbinary_node(entry); 58 | node->left->parent = node; 59 | } 60 | else { 61 | CBTreeNode node = get_node_by_index(tree, pos / 2); 62 | node->right = create_cbinary_node(entry); 63 | node->right->parent = node; 64 | } 65 | tree->size++; 66 | } 67 | 68 | // remove the last entry from a cbt 69 | BTEntry remove_last(CBTree tree) { 70 | int pos = tree->size; 71 | assert (pos > 0); 72 | CBTreeNode node = get_node_by_index(tree, pos); 73 | if (node->parent != NULL) { 74 | if (pos % 2 == 0) { 75 | node->parent->left = NULL; 76 | } else { 77 | node->parent->right = NULL; 78 | } 79 | } 80 | BTEntry to_return = node->value; 81 | free(node); 82 | tree->size--; 83 | return to_return; 84 | } 85 | 86 | //============= Binary heap functions =========================// 87 | 88 | // Create a heap entry 89 | HeapEntry create_heap_entry(char* key, int priority) { 90 | HeapEntry new_entry = malloc(sizeof(*new_entry)); 91 | new_entry->key = key; 92 | new_entry->priority = priority; 93 | 94 | return new_entry; 95 | } 96 | 97 | // Create a heap, simply by creating a complete bt 98 | Heap create_heap(DestroyFunc destroy) { 99 | return create_CBTree(destroy); 100 | } 101 | 102 | void heapify_up(Heap heap, HeapNode node) { 103 | // if the root is null, there is nothing to do 104 | if (heap->root == NULL) 105 | return; 106 | HeapNode parent; 107 | // we continue in this loop while the child has bigger priority than the parent 108 | // thus we need to swap them 109 | while ((parent = node->parent) != NULL) { 110 | HeapEntry parent_entry = parent->value; 111 | HeapEntry node_entry = node->value; 112 | // done! the heap property is restored! 113 | if (parent_entry->priority > node_entry->priority) 114 | return; 115 | else if (parent_entry->priority == node_entry->priority) { 116 | if (strcmp(parent_entry->key, node_entry->key) < 0) 117 | return; 118 | } 119 | // performing the neccesary swapping 120 | parent->value = node_entry; 121 | node->value = parent_entry; 122 | // we move up one level 123 | node = parent; 124 | } 125 | } 126 | 127 | void heapify_down(Heap heap, HeapNode node) { 128 | // Nothing to be done if the node that we want to begin the heapify from is null! 129 | if (node == NULL) 130 | return; 131 | HeapNode child; 132 | // we continue in this loop while the child has bigger priority than the parent 133 | // thus we need to swap them 134 | while ((child = node->left) != NULL) { 135 | // we naively choose the left child to compare. gonna change that very soon... 136 | HeapEntry child_entry = child->value; 137 | HeapEntry node_entry = node->value; 138 | 139 | // The right node might have bigger priority than the left, so we check that (here, we've changed our naive assumption) 140 | HeapNode right = node->right; 141 | if ((right != NULL) && right->value->priority > child->value->priority) { 142 | child = right; 143 | child_entry = right->value; 144 | } else if ((right != NULL) && (right->value->priority == child->value->priority) && strcmp(right->value->key, child->value->key) < 0) { 145 | child = right; 146 | child_entry = right->value; 147 | } 148 | // done! the heap priority is restored! 149 | if (node->value->priority > child->value->priority) 150 | return; 151 | else if ((node->value->priority == child->value->priority) && strcmp(node->value->key, child->value->key) < 0) 152 | return; 153 | // performing the neccesary swapping 154 | node->value = child_entry; 155 | child->value = node_entry; 156 | // we move down one level 157 | node = child; 158 | } 159 | } 160 | 161 | // Insert a new (key, priority) pair into the heap 162 | void heap_insert(Heap heap, int priority, char* key) { 163 | // Create the entry 164 | HeapEntry entry = create_heap_entry(key, priority); 165 | // insert it as the last item in the heap 166 | CBTree_insert(heap, entry); 167 | // heapify so the heap priority is preserved 168 | heapify_up(heap, get_node_by_index(heap, heap->size)); 169 | } 170 | 171 | HeapNode get_nth_node(Heap heap, int i) { 172 | return get_node_by_index(heap, i); 173 | } 174 | 175 | // Pop the item of the heap with the biggest priority 176 | HeapEntry pop(Heap heap) { 177 | // Get access to the root of the heap 178 | HeapNode root = heap->root; 179 | assert(root != NULL); 180 | // Hold the value we want to return 181 | HeapEntry to_return = root->value; 182 | // remove the last value of the heap, in order to place it in the root 183 | HeapEntry last = remove_last(heap); 184 | // if there are still items in the heap 185 | if (heap->size != 0) { 186 | // assign the last item into the root 187 | root->value = last; 188 | // heapify so the heap priority is preserved 189 | heapify_down(heap, heap->root); 190 | } 191 | // return the item with the biggest priority that we've collected 192 | return to_return; 193 | } 194 | 195 | void destroy_heap(Heap heap) { 196 | while (heap->size != 0) { 197 | HeapEntry entry = pop(heap); 198 | if (heap->destroy != NULL) 199 | heap->destroy(entry); 200 | free(entry); 201 | } 202 | free(heap); 203 | } -------------------------------------------------------------------------------- /DiseaseAggregator/modules/LinkedList.c: -------------------------------------------------------------------------------- 1 | #include "../include/LinkedList.h" 2 | 3 | // Create and initialize a new list 4 | List create_list(CompareFunc compare, DestroyFunc destroy) { 5 | List new_list = malloc(sizeof(*new_list)); 6 | new_list->head = NULL; 7 | new_list->size = 0; 8 | new_list->compare = compare; 9 | new_list->destroy = destroy; 10 | 11 | return new_list; 12 | } 13 | 14 | // Insert an item in the end of a list 15 | void list_insert(List list, Pointer node_info) { 16 | // Special behavior if a list is empty 17 | if (list->size == 0) { 18 | list->head = malloc(sizeof(*list->head)); 19 | list->head->value = node_info; 20 | list->head->next = NULL; 21 | list->size++; 22 | return; 23 | } 24 | ListNode temp = list->head; 25 | while ( temp->next != NULL) { 26 | temp = temp->next; 27 | } 28 | 29 | ListNode new_node = malloc(sizeof(*new_node)); 30 | new_node->value = node_info; 31 | new_node->next = NULL; 32 | temp->next = new_node; 33 | list->size++; 34 | } 35 | 36 | // Search for a specific entry into the list 37 | bool list_search(List list, Pointer node_info) { 38 | ListNode temp = list->head; 39 | 40 | while (temp != NULL) { 41 | if (list->compare(temp->value, node_info) == 0) { 42 | return true; 43 | } 44 | temp = temp->next; 45 | } 46 | return false; 47 | } 48 | 49 | // Function to see if an item exists in a list 50 | bool in_list(List list, Pointer node_info) { 51 | return list_search(list, node_info); 52 | } 53 | 54 | // Return the size of the list 55 | int list_size(List list) { 56 | return list->size; 57 | } 58 | 59 | // Return the nth item of a list 60 | Pointer list_nth(List list, int n) { 61 | ListNode temp = list->head; 62 | while (temp) { 63 | if (n == 0) { 64 | return temp->value; 65 | } 66 | temp = temp->next; n--; 67 | } 68 | // If we reach this point, then the item is not on the list, thus return NULL 69 | return NULL; 70 | } 71 | 72 | // check if a list is empty 73 | bool is_empty(List list) { 74 | return (list->size == 0); 75 | } 76 | // Destroy a list by freeing all of the memory occupied 77 | void destroy_list(Pointer l) { 78 | List list = (List)l; 79 | ListNode temp = list->head; 80 | while (temp != NULL) { 81 | ListNode next = temp->next; 82 | if (list->destroy) { 83 | list->destroy(temp->value); 84 | } 85 | free(temp); 86 | temp = next; 87 | } 88 | free(list); 89 | } 90 | -------------------------------------------------------------------------------- /DiseaseAggregator/modules/Patient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Patient.h" 5 | #include "Dates.h" 6 | 7 | extern Pointer empty; 8 | // Given a string, create a new patient record 9 | Patient* create_patient(char* str, char* country, char* entry_str) { 10 | char* entry_date = strdup(entry_str); 11 | Patient* p = malloc(sizeof(*p)); 12 | assert(p != NULL); 13 | // Our delimiter will be the space character 14 | char delim[3] = " \n"; 15 | char* id = strdup(strtok(str, delim)); 16 | char* action = strdup(strtok(NULL, delim)); 17 | // just ignore the action, we've checked it earlier 18 | free(action); 19 | char* first_name = strdup(strtok(NULL, delim)); 20 | char* last_name = strdup(strtok(NULL, delim)); 21 | char* disease = strdup(strtok(NULL, delim)); 22 | int age = atoi(strtok(NULL, delim)); 23 | if (id == NULL || first_name == NULL || last_name == NULL 24 | || disease == NULL || country == NULL || entry_date == NULL || 25 | age < 0 || age > 120) { 26 | free(p); 27 | return NULL; 28 | } 29 | Date entry = string_to_date(entry_date); 30 | free(entry_date); 31 | p->id = id; 32 | p->first_name = first_name; 33 | p->last_name = last_name; 34 | p->disease = disease; 35 | p->country = country; 36 | p->age = age; 37 | p->entry_date = entry; 38 | // the exit day is null for the time being 39 | p->exit_date = string_to_date("-"); 40 | // Return the desired struct 41 | return p; 42 | } 43 | 44 | // Destroy a patient, in order to free the allocated memory 45 | void destroy_patient(Pointer p) { 46 | Patient* patient = (Patient*)p; 47 | if (patient != empty && patient != NULL) { 48 | free (patient->id); 49 | free(patient->first_name); 50 | free(patient->last_name); 51 | free(patient->disease); 52 | free(patient->country); 53 | 54 | free(patient); 55 | } 56 | } -------------------------------------------------------------------------------- /DiseaseAggregator/modules/common_functions.c: -------------------------------------------------------------------------------- 1 | #include "common_functions.h" 2 | #include 3 | #include 4 | 5 | // compare function for strings 6 | int compare_strings (Pointer a, Pointer b) { 7 | return strcmp((char*) a, (char*)b); 8 | } 9 | 10 | // compare function for our entries 11 | int compare(Pointer first, Pointer second) { 12 | TreeEntry entry1 = (TreeEntry)first; 13 | TreeEntry entry2 = (TreeEntry)second; 14 | return compare_dates(entry1->date, entry2->date); 15 | } 16 | 17 | // remake of the concat function because c is useless 18 | char* concat(char* str1, char* str2) { 19 | char* res = malloc(strlen(str1) + strlen(str2) + 10); 20 | memcpy(res, str1, strlen(str1)); 21 | memcpy(res + strlen(str1), str2, strlen(str2) + 1); 22 | return res; 23 | } 24 | 25 | // Remake of the itoa function, because gcc sucks 26 | char* itoa(int n) { 27 | char* res = malloc(10); 28 | snprintf(res, 10,"%d", n); 29 | return res; 30 | } 31 | 32 | // djb2 hash function, simple and quick 33 | uint hash_strings(Pointer value) { 34 | uint hash = 5381; 35 | for (char* s = value; *s != '\0'; s++) 36 | hash = (hash << 5) + hash + *s; 37 | return hash; 38 | } 39 | 40 | // Count how many lines there are in a file 41 | int nlines(FILE* input) { 42 | int n_lines = 0; 43 | while (!feof(input)) { 44 | char ch = fgetc(input); 45 | if (ch == '\n') { 46 | n_lines++; 47 | } 48 | } 49 | rewind(input); 50 | return n_lines; 51 | } 52 | 53 | // Get the number of the words in a string 54 | int n_words(char* s) { 55 | char delim[3] = " \n"; 56 | int n = 0; 57 | char* str = strdup(s); 58 | char* res = strtok(str, delim); 59 | // read until we reach null 60 | while (res) { 61 | n++; 62 | res = strtok(NULL, delim); 63 | } 64 | // no leaks here 65 | free (str); 66 | return n; 67 | } 68 | 69 | // Get the n-th word of a string 70 | char* nth_word(char* s, int n) { 71 | char* str = strdup(s); 72 | int nw = n_words(str); 73 | char* arr[nw]; 74 | char delim[3] = " \n"; 75 | arr[0] = strtok(str, delim); 76 | int i = 1; 77 | while (i < n) { 78 | arr[i] = strtok(NULL, delim); 79 | i++; 80 | } 81 | char* ret = strdup(arr[i-1]); 82 | free(str); 83 | return ret; 84 | } 85 | 86 | // Read a string from a pipe 87 | char* read_from_pipe(int fd, int buff_size) { 88 | // find out how many bytes we want to read 89 | int n_bytes; 90 | int n = read(fd, &n_bytes, sizeof(int)); 91 | // if read fails due to an interupt, then just return null, we'll handle it later 92 | if (n == -1 && (errno == EINTR)) { 93 | return NULL; 94 | } 95 | // Allocate a string to return (space for \0 is taken into account by the write function) 96 | char* info = malloc((n_bytes + 1) * sizeof(*info)); 97 | // set how many times we must read from the buffer given the buff_size 98 | int times = (n_bytes / buff_size) + 1; 99 | for (int i = 0; i < times; i++) { 100 | // the first n-1 times, read the full message 101 | if (i < times -1) { 102 | char* current = info + (i * buff_size); 103 | read(fd, current, buff_size); 104 | } else { 105 | // the last time, read the remainder of the bytes in the file (if it is grater than 0) 106 | int remainder = n_bytes % buff_size; 107 | if (remainder) { 108 | char* current = info + (i * buff_size); 109 | read(fd, current, remainder); 110 | } 111 | } 112 | } 113 | // last byte of the new string must be \0 114 | info[n_bytes] = '\0'; 115 | // finally, return the whole message 116 | return info; 117 | } 118 | 119 | // Write a sting to the pipe, by writing buff_size bytes each time 120 | void write_to_pipe(int fd, int buff_size, char* info) { 121 | // exit violently if a buffsize of 0 is given 122 | assert(buff_size > 0); 123 | // Hold the number of the bytes in the message, so the reader will read easily 124 | int n_bytes = strlen(info); 125 | // Write the n of bytes in front of the message 126 | write(fd, &n_bytes, sizeof(int)); 127 | // set how many times we must write to the buffer given the buff_size 128 | int times = (strlen(info) / buff_size) + 1; 129 | for (int i = 0; i < times; i++) { 130 | // the first n-1 times, write the full message 131 | if (i < times - 1) { 132 | char* current = info + (i * buff_size); 133 | write(fd, current, buff_size); 134 | } 135 | else { 136 | // the last time, write the remainder of the bytes in the file (if it is grater than 0) 137 | int remainder = n_bytes % buff_size; 138 | if (remainder) { 139 | char* current = info + (i * buff_size); 140 | write(fd, current, (remainder)); 141 | } 142 | } 143 | } 144 | } 145 | 146 | // Function to traverse our dirs_to_workers hash table 147 | void print_list_contents(Pointer ent, Pointer d1, Pointer d2, Pointer d3, Pointer d4) { 148 | HashEntry entry = (HashEntry)ent; 149 | if (entry) { 150 | printf("%15s %d\n", entry->key, *(int*)entry->item); 151 | } 152 | } 153 | // Function to traverse our dirs_to_workers hash table 154 | void print_countries(Pointer ent, Pointer f, Pointer d2, Pointer d3, Pointer d4) { 155 | FILE* file = (FILE*)f; 156 | HashEntry entry = (HashEntry)ent; 157 | if (entry) { 158 | fprintf(file, "%15s\n", entry->key); 159 | } 160 | } 161 | 162 | // Assign a pid to a workers's id 163 | int get_pos_from_pid(int pid, int* workers, int n_workers) { 164 | for (int i = 0; i < n_workers; i++) { 165 | if (pid == workers[i]) 166 | return i; 167 | } 168 | return FAILED; 169 | } 170 | 171 | // find out how many files are in a directory 172 | int n_files_in_dir(char* path) { 173 | int count = 0; 174 | DIR* directory; 175 | struct dirent* entry; 176 | directory = opendir(path); 177 | while ((entry = readdir(directory)) != NULL) { 178 | if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) 179 | count++; 180 | } 181 | closedir(directory); 182 | return count; 183 | } 184 | 185 | // find out how many files a worker has 186 | int n_files_in_worker(char* path, List countries) { 187 | int count = 0; 188 | for (int i = 0; i < countries->size; i++) { 189 | count += n_files_in_dir(concat(path, (char*)list_nth(countries, i))); 190 | } 191 | return count; 192 | } 193 | -------------------------------------------------------------------------------- /DiseaseAggregator/worker/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = worker 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build_worker 6 | SRC_DIRS = . ../modules 7 | INC_DIRS = ../include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name "*.c") 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ 17 | 18 | # c source 19 | $(BUILD_DIR)/%.c.o: %.c 20 | $(MKDIR_P) $(dir $@) 21 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 22 | 23 | 24 | .PHONY: clean 25 | 26 | clean: 27 | $(RM) -r $(BUILD_DIR) 28 | $(RM) $(TARGET_EXEC) 29 | 30 | -include $(DEPS) 31 | 32 | MKDIR_P ?= mkdir -p -------------------------------------------------------------------------------- /DiseaseAggregator/worker/WorkerMenu.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "WorkerMenu.h" 3 | #include "common_functions.h" 4 | 5 | bool worker_menu(char* qu, List dirs, HashTable patients, HashTable diseases_hash, int write, int buffsize) { 6 | char* query = strdup(qu); 7 | 8 | char delim[3] = " \n"; 9 | if (strstr(query, "/diseaseFrequency")) { 10 | if (n_words(query) < 4 || n_words(query) > 5) { 11 | fprintf(stderr, "error\n"); 12 | return false; 13 | } 14 | char* q = strtok(query, delim); 15 | if (strcmp(q, "/diseaseFrequency")) { 16 | free(qu); 17 | return false; 18 | } 19 | char* virus = strtok(NULL, delim); 20 | char* arg2 = strtok(NULL, delim); 21 | char* arg3 = strtok(NULL, delim); 22 | char* country = strtok(NULL, delim); 23 | int res = 0; 24 | // if a country is specified, then just return the number 25 | if (country) { 26 | res = disease_frequency(virus, arg2, arg3, country, diseases_hash); 27 | } 28 | else { 29 | for (int i = 0; i < dirs->size; i++) { 30 | char* curr_country = list_nth(dirs,i); 31 | res += disease_frequency(virus, arg2, arg3, curr_country, diseases_hash); 32 | } 33 | free(qu); 34 | } 35 | write_to_pipe(write, buffsize, itoa(res)); 36 | return true; 37 | } 38 | else if (strstr(query, "/topk-AgeRanges")) { 39 | if (n_words(query) != 6) { 40 | fprintf(stderr, "error\n"); 41 | free(qu); 42 | return false; 43 | } 44 | char* q = strtok(query, delim); 45 | if (strcmp(q, "/topk-AgeRanges")) { 46 | free(qu); 47 | return false; 48 | } 49 | // Analyze the input 50 | int k = atoi(strtok(NULL, delim)); 51 | char* country = strtok(NULL, delim); 52 | char* disease = strtok(NULL, delim); 53 | char* day1 = strtok(NULL, delim); 54 | char* day2 = strtok(NULL, delim); 55 | topk_age_ranges(k, country, disease, day1, day2, diseases_hash, write, buffsize); 56 | return true; 57 | } 58 | else if (strstr(query, "/searchPatientRecord")) { 59 | if (n_words(query) != 2) { 60 | fprintf(stderr, "error\n"); 61 | free(qu); 62 | return false; 63 | } 64 | char* q = strtok(query, delim); 65 | if (strcmp(q, "/searchPatientRecord")) { 66 | free(qu); 67 | return false; 68 | } 69 | char* r_id = strtok(NULL, delim); 70 | char* result = search_patient_record(r_id, patients); 71 | write_to_pipe(write, buffsize, search_patient_record(r_id, patients)); 72 | if (!strcmp(result, "-")) 73 | return false; 74 | else 75 | return true; 76 | } 77 | else if (strstr(query, "/numPatientAdmissions")) { 78 | if (n_words(query) < 4 || n_words(query) > 5) { 79 | fprintf(stderr, "error\n"); 80 | free(qu); 81 | return false; 82 | } 83 | char* q = strtok(query, delim); 84 | if (strcmp(q, "/numPatientAdmissions")) { 85 | free(qu); 86 | return false; 87 | } 88 | char* virus = strtok(NULL, delim); 89 | char* arg2 = strtok(NULL, delim); 90 | char* arg3 = strtok(NULL, delim); 91 | char* country = strtok(NULL, delim); 92 | // If the query is specified for 1 country 93 | if (country) { 94 | // just write it to the pipe 95 | char* result = num_patient_admissions(virus, arg2, arg3, country, diseases_hash); 96 | write_to_pipe(write, buffsize, result); 97 | free(result); 98 | return true; 99 | } 100 | else { 101 | // if we wna to send many responses, inform the parent how many of them we're gonna store 102 | char* n = itoa(dirs->size); 103 | write_to_pipe(write, buffsize, n); 104 | // for each one of them 105 | for (int i = 0; i < dirs->size; i++) { 106 | // call the query function with a specific country 107 | char* curr_country = list_nth(dirs, i); 108 | char* result = num_patient_admissions(virus, arg2, arg3, curr_country, diseases_hash); 109 | write_to_pipe(write, buffsize, result); 110 | free(result); 111 | } 112 | free(qu); 113 | free(n); 114 | return true; 115 | } 116 | } 117 | else if (strstr(query, "/numPatientDischarges")) { 118 | if (n_words(query) < 4 || n_words(query) > 5) { 119 | fprintf(stderr, "error\n"); 120 | free(qu); 121 | return false; 122 | } 123 | char* q = strtok(query, delim); 124 | if (strcmp(q, "/numPatientDischarges")) { 125 | free(qu); 126 | return false; 127 | } 128 | char* virus = strtok(NULL, delim); 129 | char* arg2 = strtok(NULL, delim); 130 | char* arg3 = strtok(NULL, delim); 131 | char* country = strtok(NULL, delim); 132 | // If the query is specified for 1 country 133 | if (country) { 134 | // just write it to the pipe 135 | char* result = num_patient_discharges(virus, arg2, arg3, country, diseases_hash); 136 | write_to_pipe(write, buffsize, result); 137 | free(result); 138 | return true; 139 | } 140 | else { 141 | // if we wna to send many responses, inform the parent how many of them we're gonna store 142 | char* n = itoa(dirs->size); 143 | write_to_pipe(write, buffsize, n); 144 | // for each one of them 145 | for (int i = 0; i < dirs->size; i++) { 146 | // call the query function with a specific country 147 | char* curr_country = list_nth(dirs, i); 148 | char* result = num_patient_discharges(virus, arg2, arg3, curr_country, diseases_hash); 149 | write_to_pipe(write, buffsize, result); 150 | free(result); 151 | } 152 | free(qu); 153 | free(n); 154 | return true; 155 | } 156 | } else { 157 | fprintf(stderr, "Query not recognized by worker: %s\n", qu); 158 | free(qu); 159 | return false; 160 | } 161 | } -------------------------------------------------------------------------------- /DiseaseAggregator/worker/queries.c: -------------------------------------------------------------------------------- 1 | #include "Queries.h" 2 | 3 | //=================================== Hepful functions needed for the queries ===============================// 4 | 5 | // Compare function for strings 6 | int compare_ids (Pointer a, Pointer b) { 7 | return strcmp((char*) a, (char*)b); 8 | } 9 | // condition function for a tree 10 | bool check_same_country(Pointer ent, Pointer count, Pointer dummy1, Pointer dummy2) { 11 | char* country = (char*) count; 12 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 13 | if (entry!= NULL) { 14 | Patient* patient = entry->assigned_patient; 15 | if (!strcmp(patient->country, country)) 16 | return true; 17 | else 18 | return false; 19 | } else { 20 | printf("Disease not found\n"); 21 | return false; 22 | } 23 | } 24 | 25 | bool check_bigger_entry_date(Pointer ent, Pointer d, Pointer country, Pointer dummy2) { 26 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 27 | Patient* patient = entry->assigned_patient; 28 | Date* date = (Date*)d; 29 | return (compare_dates(patient->entry_date, *date) > 0 && !strcmp(country, patient->country)); 30 | 31 | } 32 | 33 | bool check_bigger_exit_date(Pointer ent, Pointer d, Pointer country, Pointer dummy2) { 34 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 35 | Patient* patient = entry->assigned_patient; 36 | Date* date = (Date*)d; 37 | return (compare_dates(patient->exit_date, *date) > 0) && !strcmp(country, patient->country); 38 | 39 | } 40 | 41 | bool check_age_group(Pointer ent, Pointer a, Pointer d1, Pointer d2) { 42 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 43 | Patient* patient = entry->assigned_patient; 44 | Date* date1 = (Date*)d1; 45 | Date* date2 = (Date*)d2; 46 | int age = atoi((char*)a); 47 | return (patient->age < age && compare_dates(patient->entry_date, *date1) > 0 && compare_dates(*date2, patient->entry_date)); 48 | } 49 | //========================================= Queries for the monitor =========================================// 50 | 51 | // Given a patient record, we want to mark his exit date. On success we return true, otherwise false 52 | bool recordPatientExit(char* info, HashTable patients, char* exit_str) { 53 | char* exit_d = strdup(exit_str); 54 | char delim[3] = " \n"; 55 | char* r_id = strtok(info, delim); 56 | if (r_id == NULL || exit_d == NULL) { 57 | return false; 58 | } 59 | // Mark the exit date 60 | Date exit_date = string_to_date(exit_d); 61 | free(exit_d); 62 | // Search for the patient 63 | HashEntry patient_entry = hash_search(patients, r_id); 64 | if (patient_entry == NULL) { 65 | return false; 66 | } 67 | // The patient record can be easily found in the patients ht, where a pointer to the record is kept 68 | Patient* patient = (Patient*)patient_entry->item; 69 | // Check of the patient has already exited the hospital 70 | if (check_valid_dates(patient->entry_date, exit_date)) { 71 | // Update the desired info 72 | patient->exit_date = exit_date; 73 | return true; 74 | } else { 75 | return false; 76 | } 77 | } 78 | 79 | // This query is called by each worker for each country, thus the country argument will always be present 80 | int disease_frequency(char* virus, char* arg2, char* arg3, char* country, HashTable diseases_hash) { 81 | char* a2 = strdup(arg2); 82 | char* a3 = strdup(arg3); 83 | 84 | Date d1 = string_to_date(a2); 85 | Date d2 = string_to_date(a3); 86 | free(a2); free(a3); 87 | if (!check_valid_dates(d1, d2)) { 88 | fprintf(stderr, "Wrong dates\n"); 89 | return FAILED; 90 | } 91 | HashEntry entry = hash_search(diseases_hash, virus); 92 | if (entry == NULL) { 93 | printf("Desired disease not found\n"); 94 | return FAILED; 95 | } else { 96 | // Insert the same country as a condition to the previous function. 97 | BalancedTree tree = entry->item; 98 | int g_than = total_nodes_grater_than(tree, &d1, check_same_country, country) - total_nodes_grater_than(tree, &d2, check_same_country, country); 99 | return g_than; 100 | } 101 | } 102 | 103 | char* search_patient_record(char* r_id, HashTable patients) { 104 | // search for the wanted record in the patients hash 105 | HashEntry result = hash_search(patients, r_id); 106 | // if we do not find it just return NULL 107 | if (result == NULL) 108 | return "-"; 109 | // else, construct the appropriate string to store the patient record 110 | Patient* p = result->item; 111 | char* entry_date = date_to_string(p->entry_date); 112 | char* exit_date = date_to_string(p->exit_date); 113 | char* patient = malloc((sizeof(*p) + 10) * sizeof(*patient)); 114 | snprintf(patient, (sizeof(*p) + 10), "%s %s %s %s %d %s %s", r_id, p->first_name, p->last_name, p->disease, p->age, entry_date, exit_date); 115 | // return that string 116 | return patient; 117 | } 118 | 119 | char* num_patient_admissions(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash) { 120 | // Convert the input strings to dates 121 | char* a2 = strdup(arg2); 122 | char* a3 = strdup(arg3); 123 | Date d1 = string_to_date(a2); 124 | Date d2 = string_to_date(a3); 125 | free(a2); free(a3); 126 | BalancedTree disease_tree = hash_search(diseases_hash, disease)->item; 127 | // If there is no such entry in the disease ht, then we do not have any patients here 128 | if (disease_tree == NULL) { 129 | return NULL; 130 | } 131 | // all the ones that are after date 2, except those that are after date 1 132 | int res = balanced_tree_cond_traverse(disease_tree, check_bigger_entry_date, &d1, country, NULL) - balanced_tree_cond_traverse(disease_tree, check_bigger_entry_date, &d2, country, NULL); 133 | 134 | int len = strlen(country) + strlen(itoa(res)) + 3; 135 | char* to_return = malloc(len * sizeof(*to_return)); 136 | sprintf(to_return, "%15s %d\n", country, res); 137 | return to_return; 138 | } 139 | 140 | char* num_patient_discharges(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash) { 141 | // Convert the input strings to dates 142 | char* a2 = strdup(arg2); 143 | char* a3 = strdup(arg3); 144 | Date d1 = string_to_date(a2); 145 | Date d2 = string_to_date(a3); 146 | free(a2); free(a3); 147 | BalancedTree disease_tree = hash_search(diseases_hash, disease)->item; 148 | // If there is no such entry in the disease ht, then we do not have any patients here 149 | if (disease_tree == NULL) { 150 | return NULL; 151 | } 152 | // all the ones that are after date 2, except those that are after date 1 153 | int res = balanced_tree_cond_traverse(disease_tree, check_bigger_exit_date, &d1, country, NULL) - balanced_tree_cond_traverse(disease_tree, check_bigger_exit_date, &d2, country, NULL); 154 | int len = strlen(country) + strlen(itoa(res)) + 3; 155 | char* to_return = malloc(len * sizeof(*to_return)); 156 | sprintf(to_return, "%15s %d\n", country, res); 157 | return to_return; 158 | } 159 | 160 | void topk_age_ranges(int k, char* country, char* disease, char* day1, char* day2, HashTable diseases_hash, int write, int buff_size) { 161 | // Convert the input strings to dates 162 | Date d1 = string_to_date(day1); 163 | Date d2 = string_to_date(day2); 164 | BalancedTree disease_tree = hash_search(diseases_hash, disease)->item; 165 | int age_gr1 = balanced_tree_cond_traverse(disease_tree, check_age_group, "20", &d1, &d2); 166 | int age_gr2 = balanced_tree_cond_traverse(disease_tree, check_age_group, "40", &d1, &d2); 167 | int age_gr3 = balanced_tree_cond_traverse(disease_tree, check_age_group, "60", &d1, &d2); 168 | int age_gr4 = balanced_tree_cond_traverse(disease_tree, check_age_group, "120", &d1, &d2); 169 | int total = age_gr1 + age_gr2 + age_gr3 + age_gr4; 170 | Heap heap = create_heap(NULL); 171 | heap_insert(heap, age_gr1, "0-20"); 172 | heap_insert(heap, age_gr2, "20-40"); 173 | heap_insert(heap, age_gr3, "40-60"); 174 | heap_insert(heap, age_gr4, "60+"); 175 | // define how many times we will write in the pipes, and send this number so the parent is aware too 176 | 177 | int k_ret = (k < 4) ? k : 4; 178 | write_to_pipe(write, buff_size, itoa(k_ret)); 179 | char* ret = malloc(13 * sizeof(*ret)); 180 | for (int i = 0; i key, 100 * ent->priority / total); 183 | write_to_pipe(write, buff_size, ret); 184 | free(ent); 185 | } 186 | free(ret); 187 | destroy_heap(heap); 188 | } 189 | -------------------------------------------------------------------------------- /DiseaseAggregator/worker/worker.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "common_functions.h" 3 | #include "HashTable.h" 4 | #include "BalancedTree.h" 5 | #include "Patient.h" 6 | #include "LinkedList.h" 7 | #include "WorkerMenu.h" 8 | #include 9 | #include "Queries.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "parser.h" 16 | 17 | volatile sig_atomic_t sig_int_raised; 18 | volatile sig_atomic_t sig_usr1_raised; 19 | 20 | void catch_int(int signo) { 21 | sig_int_raised = signo; 22 | } 23 | 24 | void catch_usr1(int signo) { 25 | sig_usr1_raised = signo; 26 | } 27 | 28 | int main(int argc, char* argv[]) { 29 | fprintf(stderr, "child %d\n", getpid()); 30 | // iniialize a signal set 31 | static struct sigaction act_int; 32 | // handle our signals with our function, in order to catch them 33 | act_int.sa_handler = catch_int; 34 | sigfillset(&(act_int.sa_mask)); 35 | // want to handle SIGINT and SIGQUIT with this function 36 | sigaction(SIGINT, &act_int, NULL); 37 | sigaction(SIGQUIT, &act_int, NULL); 38 | // we must also handle a SIGUSR1 39 | static struct sigaction act_usr; 40 | // handle our signals with our function, in order to catch them 41 | act_usr.sa_handler = catch_usr1; 42 | sigfillset(&(act_usr.sa_mask)); 43 | // want to handle SIGINT and SIGQUIT with this function 44 | sigaction(SIGUSR1, &act_usr, NULL); 45 | 46 | // Get the pipe names from the args, and open them: 47 | // 1st for writing, 2nd for reading 48 | int reading, writing; 49 | int buff_size = atoi(argv[3]); 50 | char* input_dir = concat(argv[4], "/"); 51 | writing = open(argv[1], O_WRONLY, 0666); 52 | reading = open(argv[2], O_RDONLY, 0666); 53 | // check for possible errors while opening the pipes 54 | if (reading == -1 || writing == -1) { 55 | perror("open"); 56 | } 57 | // if "init is given, then during parsing we must print the stats" 58 | bool print_stats = (strcmp(argv[5], "init") == 0); 59 | // Variables to stroe statistics for records. 60 | int success = 0; int failed = 0; 61 | // Create a hash table to store all the different diseases 62 | HashTable diseases_hash = hash_create(HASH_SIZE, hash_strings, BUCKET_SIZE, balanced_tree_destroy); 63 | // We are going to use one extra hashtable, in order to quickly search if the patient allready exists 64 | // We are going to pass destroy patient as a destroy func, in order to free all the memory occupied 65 | // by the patients when we are done. 66 | HashTable patients = hash_create(HASH_SIZE, hash_strings, sizeof(Patient) + sizeof(Pointer), destroy_patient); 67 | // create a list to store all the directories that the worker must handle 68 | List dirs = create_list(compare_strings, free); //TODO: Maybe NULL for destroy 69 | // create a list to hold all the files that are parsed, so we dont read them again when the user sends a SIGUSR1 70 | List parsed_files = create_list(compare_strings, NULL); //TODO: Maybe free 71 | char* str; 72 | // read the dirs from the pipe 73 | while (true) { 74 | str = read_from_pipe(reading, buff_size); 75 | // break when "end" is sent by the parent 76 | if (! strcmp(str, "end")) 77 | break; 78 | fprintf(stderr, "%s\n", str); 79 | list_insert(dirs, str); 80 | 81 | } 82 | // proceed only if the worker is assigned with at least one country 83 | if (!is_empty(dirs)) { 84 | // call the function to parse the whole input and send the stats back to the parent 85 | parser(input_dir, buff_size, dirs, parsed_files, writing, patients, diseases_hash, &success, &failed, print_stats, false); 86 | // read queries until we break 87 | while (true) { 88 | // read the instruction from the pipe 89 | char* query = read_from_pipe(reading, buff_size); 90 | // check for a possible signal 91 | // If a sigint or sigquit are caught 92 | if (sig_int_raised) { 93 | fprintf(stderr, "int caught in main\n"); 94 | // Just goto the exiting procedure 95 | goto EXIT_IF; 96 | } 97 | // if a sigusr1 is raised 98 | if (sig_usr1_raised) { 99 | fprintf(stderr, "usr1 caught in main\n"); 100 | // parse the correct files, by giving the parsed list so we do not read all the files again 101 | parser(input_dir, buff_size, dirs, parsed_files, writing, patients, diseases_hash, &success, &failed, true, true); 102 | // inform the parent that we've read stuff by sending a sigusr2 103 | kill(getppid(), SIGUSR2); 104 | // restore the value of the gloval signal variable 105 | sig_usr1_raised = 0; 106 | } 107 | if (query != NULL) { 108 | // check if an exit command is given 109 | if (strstr(query, "/exit")) { 110 | EXIT_IF: fprintf(stderr, "Child exiting\n"); 111 | // TODO: Maybe add to a function instead 112 | // free the memory occupied by our data structures 113 | hash_destroy(diseases_hash); 114 | // hash_destroy(patients); 115 | 116 | // create a directory to store our log files 117 | mkdir("./logs", PERMS); 118 | 119 | // create a log file to store what we've achieved 120 | char* f_name = concat("./logs/log_file.", itoa(getpid())); 121 | FILE* log_file = fopen(f_name, "w+"); 122 | // free(f_name); 123 | if (log_file == NULL) { 124 | perror("creating"); 125 | exit(EXIT_FAILURE); 126 | } 127 | // print all the dirs that we handled 128 | for (int i = 0; i < dirs->size; i++) { 129 | char* country = list_nth(dirs, i); 130 | fprintf(log_file, "%s\n", country); 131 | } 132 | // write the total query stats 133 | fprintf(log_file, "\nTOTAL %d\nSUCCESS %d\n FAILED %d\n", (success + failed), success, failed); 134 | // close the log file descriptor 135 | fclose(log_file); 136 | // free the list of the countries given 137 | destroy_list(dirs); 138 | // inform the parent that we've ended our job 139 | write_to_pipe(writing, buff_size, "ready"); 140 | // close the pipes 141 | close(reading); close(writing); 142 | // if we are here because of an interupt, just exit 143 | if (sig_int_raised) 144 | exit(EXIT_SUCCESS); 145 | // Finally, wait forever until the parent sends a SIGKILL that will exit the worker 146 | while(true); 147 | } 148 | // call the menu to analyze the query and return the result 149 | bool result = worker_menu(query, dirs, patients, diseases_hash, writing, buff_size); 150 | // check if an error has occured (returned null), thus we must not write in the pipe 151 | if (result) { 152 | success++; 153 | } else { 154 | failed++; 155 | } 156 | } 157 | } 158 | } else { 159 | // if no countries are assigned, just wait until the parent sends a SIGKILL 160 | while(true); 161 | } 162 | } -------------------------------------------------------------------------------- /DiseaseMonitor/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = disease_monitor 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build 6 | SRC_DIRS = ./programs ./modules 7 | INC_DIRS = ./include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name *.c) 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(BUILD_DIR)/$(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ 17 | 18 | # c source 19 | $(BUILD_DIR)/%.c.o: %.c 20 | $(MKDIR_P) $(dir $@) 21 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 22 | 23 | 24 | .PHONY: clean 25 | 26 | clean: 27 | $(RM) -r $(BUILD_DIR) 28 | 29 | run: 30 | ./$(BUILD_DIR)/$(TARGET_EXEC) 31 | 32 | valgrind: 33 | valgrind ./$(BUILD_DIR)/$(TARGET_EXEC) 34 | 35 | 36 | -include $(DEPS) 37 | 38 | MKDIR_P ?= mkdir -p 39 | -------------------------------------------------------------------------------- /DiseaseMonitor/README.md: -------------------------------------------------------------------------------- 1 | # Disease Monitor 2 | 3 | Application to proccess and answer queries based on disease cases. A file of patient records is given as an input, and is analysed using several data structures. Then, the user might ask some questions in the system. 4 | 5 | ## Available Queries 6 | 7 | #### /globalDiseaseStats [date1 date2] 8 | 9 | Returns for every virus recorded, the number of patients recorded in the system, optionally between 2 dates 10 | 11 | 12 | #### /diseaseFrequency virusName date1 date2 [country] 13 | 14 | Returns for every virus recorded, the number of patients recorded between 2 dates, optionally, in a specified countries. 15 | 16 | #### /topk-Diseases k country [date1 date2] 17 | 18 | Returns the top k diseases that are recorded in the country sepcified, allong with the number of cases, optionally between the 2 dates given. 19 | 20 | #### /topk-Countries k disease [date1 date2] 21 | 22 | Returns the top k countries that the specified disease is found, allong with the number of cases, optionally between the 2 dates given. 23 | 24 | #### /insertPatientRecord recordID patientFirstName patientLastName diseaseID country entryDate [exitDate] 25 | 26 | Inserts a new patient record in the systems 27 | 28 | #### /recordPatientExit recordID exitDate 29 | 30 | Adds an exit date to the specified patient 31 | 32 | #### /numCurrentPatients [disease] 33 | 34 | Prints the currently hospitalized patients, optionally with a specified disease 35 | 36 | ## Modules 37 | 38 | Several data structures have been implemented (can be found in the modules directory), that ensure the best possible complexity for storing the data, and asking questions. 39 | 40 | ## Input type 41 | 42 | An patient record could do like this 43 | ` 889 Mary Smith COVID-2019 China 25-1-2020 27-1-2020` 44 | 45 | ## Input files 46 | 47 | In the directory `input`, several input files can be found 48 | 49 | ## Getting Started 50 | 51 | Make sure you have a gcc compiler 52 | 53 | ### Downloading 54 | 55 | Download source code by typing: 56 | 57 | ``` git clone https://github.com/nikosgalanis/B-Plus-Tree.git ``` 58 | 59 | ## Compiling & running 60 | 61 | - Compile the project simply by typing `make` 62 | - Run like this: `build/diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries 63 | –h2 countryHashtableNumOfEntries –b bucketSize` 64 | 65 | where bucketsize stands for the hash table node size in bytes. 66 | -------------------------------------------------------------------------------- /DiseaseMonitor/include/BalancedTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BinaryTree.h" 3 | #include "Dates.h" 4 | 5 | 6 | // Our entries to our bst will be of this form 7 | struct tree_entry { 8 | Date date; 9 | Pointer assigned_patient; 10 | }; 11 | typedef struct tree_entry* TreeEntry; 12 | 13 | typedef BinaryTree BalancedTree; 14 | typedef TreeEntry BalancedTreeEntry; 15 | 16 | BalancedTreeEntry create_balanced_tree_entry(Date date, Pointer assgn); 17 | TreeNode create_tree_node(BalancedTreeEntry value); 18 | BalancedTree create_balanced_tree(CompareFunc compare, DestroyFunc destroy); 19 | void balanced_tree_insert(BalancedTree tree, BalancedTreeEntry value); 20 | int total_nodes_grater_than(BalancedTree tree, Pointer x, ConditionFunc cond, char* cond_item); 21 | int grater_than(TreeNode node, Pointer x, CompareFunc compare, ConditionFunc cond, char* cond_item); 22 | int balanced_tree_cond_traverse(BalancedTree tree, ConditionFunc cond); 23 | int node_cond_traverse(TreeNode node, ConditionFunc cond); 24 | void balanced_tree_traverse(BalancedTree tree, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p); 25 | void node_traverse(TreeNode node, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p); 26 | void balanced_tree_destroy(Pointer tree); 27 | void destroy_node(TreeNode node, DestroyFunc destroy); 28 | -------------------------------------------------------------------------------- /DiseaseMonitor/include/BinaryTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TreeTypes.h" 4 | 5 | typedef struct bs_tree_node* TreeNode; 6 | 7 | struct bs_tree_node { 8 | TreeNode left, right; 9 | Pointer value; 10 | uint height; 11 | }; 12 | 13 | typedef Tree BinaryTree; 14 | 15 | TreeNode create_binary_node(Pointer item); 16 | TreeNode insert_binary_node(TreeNode node, CompareFunc compare, Pointer item); 17 | BinaryTree create_binary_tree(CompareFunc compare, DestroyFunc destroy); 18 | void binary_tree_insert(BinaryTree tree, Pointer item); 19 | void destroy_node(TreeNode node, DestroyFunc destroy); -------------------------------------------------------------------------------- /DiseaseMonitor/include/CompareFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | #include "BalancedTree.h" 4 | #include "Dates.h" 5 | 6 | int compare(Pointer first, Pointer second) { 7 | TreeEntry entry1 = (TreeEntry)first; 8 | TreeEntry entry2 = (TreeEntry)second; 9 | return compare_dates(entry1->date, entry2->date); 10 | } -------------------------------------------------------------------------------- /DiseaseMonitor/include/Dates.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | typedef struct date { 6 | int day; 7 | int month; 8 | int year; 9 | } Date; 10 | 11 | #define EOF_DATE (Date)0 12 | 13 | bool empty_string(char* str); 14 | Date string_to_date(char* d); 15 | int compare_dates(Date date1, Date date2); 16 | bool check_valid_dates(Date date1, Date date2); 17 | bool check_if_null_date(Date date); -------------------------------------------------------------------------------- /DiseaseMonitor/include/GlobalStructures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Includes that must be made 4 | #include "common_types.h" 5 | #include "Patient.h" 6 | #include "Dates.h" 7 | #include "HashTable.h" 8 | #include "BalancedTree.h" 9 | #include "HeapUsingCBT.h" 10 | #include "Queries.h" 11 | 12 | // Our global pointers to stroe some sizes 13 | int num_diseases, num_countries; 14 | 15 | // Our global pointers to store the hash tables 16 | HashTable diseaseHashTable; 17 | HashTable countryHashTable; 18 | HashTable patients; 19 | 20 | // number of lines in the file opened 21 | int lines; -------------------------------------------------------------------------------- /DiseaseMonitor/include/HashTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | 4 | typedef uint (*HashFunc)(Pointer); 5 | 6 | struct hash_entry { 7 | char* key; 8 | Pointer item; 9 | }; 10 | 11 | typedef struct hash_entry* HashEntry; 12 | 13 | typedef struct hash_node* HashNode; 14 | 15 | struct hash_node { 16 | HashEntry* bucket; 17 | HashNode next; 18 | }; 19 | 20 | 21 | 22 | #define HASH_EOF (HashNode)0 23 | 24 | struct hash_table { 25 | HashNode* array; 26 | int size; 27 | int items; 28 | int bucket_size; 29 | int bucket_max_entries; 30 | HashFunc hash_function; 31 | DestroyFunc destroy_items; 32 | }; 33 | 34 | typedef struct hash_table* HashTable; 35 | 36 | HashEntry create_hash_entry(char* key, Pointer item); 37 | HashTable hash_create(int size, HashFunc hash_fn, int bucket_size, DestroyFunc destroy); 38 | void hash_insert(HashTable ht, char* key, Pointer item); 39 | HashEntry hash_search(HashTable ht, char* name); 40 | void hash_traverse(HashTable ht, VisitFunc print, Pointer d1, Pointer d2, Pointer p); 41 | void hash_destroy(HashTable ht); -------------------------------------------------------------------------------- /DiseaseMonitor/include/HeapUsingCBT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TreeTypes.h" 4 | 5 | struct binary_tree_entry { 6 | char* key; 7 | int priority; 8 | }; 9 | 10 | typedef struct binary_tree_entry* BTEntry; 11 | typedef struct binary_tree_node* BTNode; 12 | 13 | struct binary_tree_node { 14 | BTNode left, right, parent; 15 | BTEntry value; 16 | }; 17 | // A Complete binary tree node, is just a node 18 | typedef BTNode CBTreeNode; 19 | 20 | // As well as a complete binary tree 21 | typedef Tree CBTree; 22 | 23 | CBTree create_CBTree(DestroyFunc destroy); 24 | CBTreeNode create_cbinary_node(Pointer item); 25 | CBTreeNode get_node_by_index (CBTree tree, int i); 26 | void CBTree_insert(CBTree tree, BTEntry entry); 27 | // void CBTree_destroy(Pointer t); 28 | 29 | // Our heap is just a complete binary tree 30 | typedef CBTree Heap; 31 | 32 | typedef BTEntry HeapEntry; 33 | typedef CBTreeNode HeapNode; 34 | 35 | HeapEntry create_heap_entry(char* key, int priority); 36 | Heap create_heap(DestroyFunc destroy); 37 | void heapify_up(Heap heap, BTNode node); 38 | void heapify_down(Heap heap, BTNode node); 39 | void heap_insert(Heap heap, int priority, char* key); 40 | HeapNode get_nth_node(Heap heap, int i); 41 | HeapEntry pop(Heap heap); 42 | void destroy_heap(Heap heap); -------------------------------------------------------------------------------- /DiseaseMonitor/include/Patient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | #include "Dates.h" 4 | 5 | typedef struct patient { 6 | char* id; 7 | char* first_name; 8 | char* last_name; 9 | char* disease; 10 | char* country; 11 | Date entry_date; 12 | Date exit_date; 13 | } Patient; 14 | 15 | Patient* create_patient(char* str); 16 | void destroy_patient(Pointer p); -------------------------------------------------------------------------------- /DiseaseMonitor/include/Queries.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void globalDiseaseStats(char* info); 4 | void diseaseFrequency(char* info); 5 | void topk_Diseases(char* info); 6 | void topk_Countries(char* info); 7 | void insertPatientRecord(char* info); 8 | void recordPatientExit(char* info); 9 | void numCurrentPatients(char* info); 10 | void exit_monitor(void); 11 | -------------------------------------------------------------------------------- /DiseaseMonitor/include/TreeTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | // Our nodes have 2 child nodes, their value, and ther height in the tree 6 | // in order for it to remain balanced 7 | 8 | struct tree { 9 | Pointer root; 10 | int size; 11 | CompareFunc compare; 12 | DestroyFunc destroy; 13 | }; 14 | 15 | typedef struct tree* Tree; -------------------------------------------------------------------------------- /DiseaseMonitor/include/common_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #define STRING_SIZE 100 8 | // Usefull typedefs 9 | typedef void* Pointer; 10 | 11 | typedef unsigned int uint; 12 | 13 | typedef int (*CompareFunc)(Pointer a, Pointer b); 14 | typedef void (*DestroyFunc)(Pointer value); 15 | typedef uint (*HashFunc)(Pointer); 16 | typedef void (*VisitFunc)(Pointer, Pointer, Pointer, Pointer, Pointer); 17 | typedef bool (*ConditionFunc)(Pointer, Pointer); 18 | 19 | 20 | void parse_input (char* file, int bucket_size); 21 | void monitor_menu(); -------------------------------------------------------------------------------- /DiseaseMonitor/input/small.txt: -------------------------------------------------------------------------------- 1 | 47 David Williams SARS-1 Denmark 30-05-2009 20-02-2020 2 | 39 Mary Sanders FLU-2018 France 12-06-2012 04-01-2017 3 | 37 Timothy Miller FLU-2018 Vietnam 22-06-2012 26-04-2017 4 | 71 Mary Hale COVID-2019 Argentina 15-03-2006 15-01-2012 5 | 0 John Jacobs EVD Switzerland 25-09-2002 02-02-2006 6 | 89 James Henderson COVID-2019 Greece 15-07-2005 - 7 | 63 Michael Mcbride EVD Switzerland 20-03-2000 23-01-2004 8 | 62 Gabriel Adams SARS-1 Egypt 12-01-2012 19-03-2017 9 | 96 Carolyn Miller MERS-COV China 14-08-2008 15-08-2008 10 | 7 Andrew Day EVD Argentina 27-11-2006 18-06-2007 11 | 65 Joseph Larsen FLU-2018 Germany 03-10-2006 24-01-2007 12 | 29 Brett Fuller SARS-1 Australia 20-12-2010 23-04-2018 13 | 23 Jennifer Huffman H1N1 Germany 27-01-2007 02-03-2018 14 | 77 Daniel Wilson MERS-COV France 16-10-2013 11-10-2015 15 | 53 James Collins FLU-2018 Denmark 09-07-2003 09-10-2014 16 | 15 Michael Anderson SARS-COV-2 USA 31-03-2007 28-12-2013 17 | 38 Crystal Mcfarland SARS-COV-2 Greece 11-01-2012 06-09-2017 18 | 8 Ray Shaw SARS-COV-2 Greece 15-08-2007 18-03-2011 19 | 57 Eric Foster MERS-COV France 24-12-2004 19-08-2008 20 | 72 Jonathan Petersen MERS-COV Australia 23-11-2009 - 21 | 16 Maria Ochoa MERS-COV Argentina 10-05-2000 11-02-2012 22 | 20 Evan Chambers COVID-2019 Russia 26-12-2005 07-04-2007 23 | 58 Michael Wood SARS-COV-2 Brazil 12-01-2003 04-10-2008 24 | 34 Victoria Blackwell SARS-COV-2 Turkey 24-03-2004 04-01-2016 25 | 41 Timothy Sullivan FLU-2018 China 31-05-2001 21-01-2002 26 | 98 Kaitlyn Wells SARS-1 Italy 27-11-2001 21-03-2013 27 | 82 Christina Ellison H1N1 Italy 29-09-2007 21-02-2020 28 | 95 Heather Green SARS-COV-2 Egypt 13-10-2004 06-04-2015 29 | 91 Lauren Fisher COVID-2019 Denmark 12-06-2005 07-05-2008 30 | 60 Mark Davenport COVID-2019 Italy 14-03-2001 21-07-2005 31 | 84 Gregory Warner MERS-COV USA 23-11-2003 16-01-2017 32 | 56 Gina Rowe H1N1 Egypt 13-05-2003 15-07-2016 33 | 40 Ashley Wilson MERS-COV Germany 02-12-2006 17-04-2013 34 | 66 Sara Ortiz SARS-1 Greece 20-03-2004 06-08-2014 35 | 11 Ryan Smith SARS-1 USA 18-12-2016 10-03-2017 36 | 33 Sarah Burns SARS-COV-2 Australia 22-01-2000 13-02-2004 37 | 21 James Pearson COVID-2019 France 02-03-2000 18-12-2015 38 | 30 Johnny Spencer SARS-1 China 01-12-2004 31-03-2019 39 | 92 Matthew Castillo EVD Greece 29-04-2006 19-03-2012 40 | 28 Anthony Wood H1N1 Russia 14-04-2007 11-03-2015 41 | 76 Janice Aguirre FLU-2018 USA 02-03-2002 13-12-2015 42 | 64 Christina Newman SARS-1 Turkey 03-12-2001 16-01-2013 43 | 43 Brooke Gordon H1N1 Switzerland 10-12-2000 16-05-2015 44 | 3 Alexa Briggs SARS-1 Vietnam 08-04-2000 04-04-2018 45 | 94 Kristin Phillips MERS-COV Denmark 01-02-2008 30-11-2009 46 | 74 Glenda Quinn COVID-2019 USA 30-03-2006 - 47 | 13 Ronald Green MERS-COV Italy 08-09-2010 21-02-2017 48 | 68 John Miller SARS-1 France 19-02-2002 15-03-2003 49 | 25 Danielle Lewis COVID-2019 Russia 17-09-2007 18-09-2010 50 | 55 Connie Armstrong MERS-COV Egypt 23-01-2007 - 51 | 5 John Gates COVID-2019 Turkey 21-06-2004 - 52 | 31 Robert Holland H1N1 Switzerland 07-01-2015 22-01-2017 53 | 2 James Fisher MERS-COV USA 21-02-2000 31-01-2016 54 | 19 Jason Meyer EVD Greece 29-10-2002 24-07-2019 55 | 12 Peggy Burgess MERS-COV France 20-05-2009 11-11-2019 56 | 75 Chase Vance EVD Russia 30-04-2015 18-10-2017 57 | 22 John Morris EVD Egypt 04-11-2013 09-08-2018 58 | 36 Levi Perez SARS-COV-2 Egypt 24-12-2013 02-05-2015 59 | 54 Jimmy Gonzalez H1N1 Russia 26-03-2014 25-07-2018 60 | 97 Brett Shaw H1N1 Turkey 27-06-2004 27-01-2007 61 | 4 Jessica Gray H1N1 Vietnam 14-07-2006 21-10-2012 62 | 1 Misty Young FLU-2018 Australia 07-10-2015 - 63 | 90 Jennifer Smith COVID-2019 Greece 18-12-2015 04-07-2017 64 | 85 Lee Lee FLU-2018 Brazil 02-03-2006 - 65 | 49 Steven Bailey EVD Italy 17-05-2011 05-08-2016 66 | 24 Michael Lane SARS-COV-2 Russia 15-03-2011 07-03-2018 67 | 61 Michael Wilson FLU-2018 Germany 27-08-2007 13-01-2018 68 | 52 Amanda Nguyen MERS-COV Brazil 11-02-2009 11-09-2018 69 | 14 Stacy Martinez SARS-COV-2 Australia 01-03-2010 - 70 | 83 Tracy Miller SARS-1 Argentina 25-04-2007 17-12-2007 71 | 79 Thomas Jackson SARS-COV-2 Australia 19-04-2006 29-11-2009 72 | 50 Emma Pitts EVD Denmark 17-05-2002 16-02-2014 73 | 88 Matthew Meyer SARS-COV-2 Germany 18-04-2004 10-05-2007 74 | 46 Gloria Valentine EVD France 09-02-2006 18-11-2016 75 | 18 George Carpenter SARS-1 China 05-02-2002 24-07-2002 76 | 48 Alyssa Ramirez SARS-1 Argentina 08-10-2007 20-03-2012 77 | 78 Lisa Ayers SARS-1 Greece 28-06-2002 01-05-2005 78 | 80 Joshua Norris H1N1 USA 29-02-2008 19-07-2013 79 | 35 Eddie Watson H1N1 Switzerland 11-04-2002 27-09-2010 80 | 42 Cynthia Brewer SARS-COV-2 China 24-06-2011 03-08-2019 81 | 70 Terri Smith MERS-COV Vietnam 30-10-2019 12-11-2019 82 | 81 Brian Anderson SARS-1 France 17-03-2006 31-10-2010 83 | 27 Jonathan Davis SARS-1 Denmark 06-08-2009 16-12-2011 84 | 86 Daniel Rose SARS-1 Denmark 02-02-2006 24-08-2006 85 | 87 Jonathan Montoya SARS-1 France 15-03-2003 11-04-2018 86 | 93 Allen Lee FLU-2018 Egypt 23-10-2010 13-10-2015 87 | 26 Ryan Compton H1N1 Brazil 28-08-2002 12-06-2004 88 | 73 Kimberly Marks EVD China 01-08-2000 08-07-2013 89 | 10 Sandra Stevens FLU-2018 Switzerland 19-08-2006 19-09-2006 90 | 67 Justin Perez COVID-2019 Italy 17-03-2011 24-07-2011 91 | 51 Brittany Gray MERS-COV Brazil 20-07-2012 09-07-2016 92 | 17 Adam Bennett COVID-2019 China 02-01-2002 16-04-2014 93 | 69 Kaitlin Hamilton SARS-COV-2 France 26-06-2007 - 94 | 99 Heather Warren SARS-1 Denmark 15-05-2000 17-03-2013 95 | 32 Christina Mullins FLU-2018 China 13-06-2007 09-04-2009 96 | 6 Jason Hurley FLU-2018 Australia 11-06-2012 20-02-2019 97 | 9 Glen Mason SARS-1 Germany 23-05-2004 28-10-2012 98 | 59 Jessica Garcia MERS-COV Greece 27-12-2008 28-10-2012 99 | 44 David Bray SARS-COV-2 Denmark 10-02-2007 21-07-2009 100 | 45 Lauren Clark SARS-1 Greece 31-10-2001 25-04-2011 101 | -------------------------------------------------------------------------------- /DiseaseMonitor/misc/small.txt: -------------------------------------------------------------------------------- 1 | 47 David Williams SARS-1 Denmark 30-05-2009 20-02-2020 2 | 39 Mary Sanders FLU-2018 France 12-06-2012 04-01-2017 3 | 37 Timothy Miller FLU-2018 Vietnam 22-06-2012 26-04-2017 4 | 71 Mary Hale COVID-2019 Argentina 15-03-2006 15-01-2012 5 | 0 John Jacobs EVD Switzerland 25-09-2002 02-02-2006 6 | 89 James Henderson COVID-2019 Greece 15-07-2005 20-03-2019 7 | 63 Michael Mcbride EVD Switzerland 20-03-2000 23-01-2004 8 | 62 Gabriel Adams SARS-1 Egypt 12-01-2012 19-03-2017 9 | 96 Carolyn Miller MERS-COV China 14-08-2008 15-08-2008 10 | 7 Andrew Day EVD Argentina 27-11-2006 18-06-2007 11 | 65 Joseph Larsen FLU-2018 Germany 03-10-2006 24-01-2007 12 | 29 Brett Fuller SARS-1 Australia 20-12-2010 23-04-2018 13 | 23 Jennifer Huffman H1N1 Germany 27-01-2007 02-03-2018 14 | 77 Daniel Wilson MERS-COV France 16-10-2013 11-10-2015 15 | 53 James Collins FLU-2018 Denmark 09-07-2003 09-10-2014 16 | 38 Crystal Mcfarland SARSCOV-2 Greece 11-01-2012 06-09-2017 17 | 57 Eric Foster MERS-COV France 24-12-2004 19-08-2008 18 | 72 Jonathan Petersen MERS-COV Australia 23-11-2009 20-03-2019 19 | 16 Maria Ochoa MERS-COV Argentina 10-05-2000 11-02-2012 20 | 20 Evan Chambers COVID-2019 Russia 26-12-2005 07-04-2007 21 | 41 Timothy Sullivan FLU-2018 China 31-05-2001 21-01-2002 22 | 98 Kaitlyn Wells SARS-1 Italy 27-11-2001 21-03-2013 23 | 82 Christina Ellison H1N1 Italy 29-09-2007 21-02-2020 24 | 95 Heather Green SARSCOV-2 Egypt 13-10-2004 06-04-2015 25 | 91 Lauren Fisher COVID-2019 Denmark 12-06-2005 07-05-2008 26 | 60 Mark Davenport COVID-2019 Italy 14-03-2001 21-07-2005 27 | 84 Gregory Warner MERS-COV USA 23-11-2003 16-01-2017 28 | 56 Gina Rowe H1N1 Egypt 13-05-2003 15-07-2016 29 | 40 Ashley Wilson MERS-COV Germany 02-12-2006 17-04-2013 30 | 66 Sara Ortiz SARS-1 Greece 20-03-2004 06-08-2014 31 | 11 Ryan Smith SARS-1 USA 18-12-2016 10-03-2017 32 | 33 Sarah Burns SARSCOV-2 Australia 22-01-2000 13-02-2004 33 | 30 Johnny Spencer SARS-1 China 01-12-2004 31-03-2019 34 | 92 Matthew Castillo EVD Greece 29-04-2006 19-03-2012 35 | 28 Anthony Wood H1N1 Russia 14-04-2007 11-03-2015 36 | 76 Janice Aguirre FLU-2018 USA 02-03-2002 13-12-2015 37 | 64 Christina Newman SARS-1 Turkey 03-12-2001 16-01-2013 38 | 43 Brooke Gordon H1N1 Switzerland 10-12-2000 16-05-2015 39 | 3 Alexa Briggs SARS-1 Vietnam 08-04-2000 04-04-2018 40 | 94 Kristin Phillips MERS-COV Denmark 01-02-2008 30-11-2009 41 | 74 Glenda Quinn COVID-2019 USA 30-03-2006 20-03-2019 42 | 13 Ronald Green MERS-COV Italy 08-09-2010 21-02-2017 43 | 25 Danielle Lewis COVID-2019 Russia 17-09-2007 18-09-2010 44 | 55 Connie Armstrong MERS-COV Egypt 23-01-2007 20-03-2019 45 | 5 John Gates COVID-2019 Turkey 21-06-2004 20-03-2019 46 | 31 Robert Holland H1N1 Switzerland 07-01-2015 22-01-2017 47 | 2 James Fisher MERS-COV USA 21-02-2000 31-01-2016 48 | 19 Jason Meyer EVD Greece 29-10-2002 24-07-2019 49 | 12 Peggy Burgess MERS-COV France 20-05-2009 11-11-2019 50 | 75 Chase Vance EVD Russia 30-04-2015 18-10-2017 51 | 22 John Morris EVD Egypt 04-11-2013 09-08-2018 52 | 36 Levi Perez SARSCOV-2 Egypt 24-12-2013 02-05-2015 53 | 54 Jimmy Gonzalez H1N1 Russia 26-03-2014 25-07-2018 54 | 97 Brett Shaw H1N1 Turkey 27-06-2004 27-01-2007 55 | 4 Jessica Gray H1N1 Vietnam 14-07-2006 21-10-2012 56 | 1 Misty Young FLU-2018 Australia 07-10-2015 20-03-2019 57 | 90 Jennifer Smith COVID-2019 Greece 18-12-2015 04-07-2017 58 | 85 Lee Lee FLU-2018 Brazil 02-03-2006 20-03-2019 59 | 49 Steven Bailey EVD Italy 17-05-2011 05-08-2016 60 | 61 Michael Wilson FLU-2018 Germany 27-08-2007 13-01-2018 61 | 52 Amanda Nguyen MERS-COV Brazil 11-02-2009 11-09-2018 62 | 14 Stacy Martinez SARSCOV-2 Australia 01-03-2010 20-03-2019 63 | 83 Tracy Miller SARS-1 Argentina 25-04-2007 17-12-2007 64 | 79 Thomas Jackson SARSCOV-2 Australia 19-04-2006 29-11-2009 65 | 50 Emma Pitts EVD Denmark 17-05-2002 16-02-2014 66 | 18 George Carpenter SARS-1 China 05-02-2002 24-07-2002 67 | 48 Alyssa Ramirez SARS-1 Argentina 08-10-2007 20-03-2012 68 | 78 Lisa Ayers SARS-1 Greece 28-06-2002 01-05-2005 69 | 80 Joshua Norris H1N1 USA 29-02-2008 19-07-2013 70 | 35 Eddie Watson H1N1 Switzerland 11-04-2002 27-09-2010 71 | 70 Terri Smith MERS-COV Vietnam 30-10-2019 12-11-2019 72 | 81 Brian Anderson SARS-1 France 17-03-2006 31-10-2010 73 | 27 Jonathan Davis SARS-1 Denmark 06-08-2009 16-12-2011 74 | 86 Daniel Rose SARS-1 Denmark 02-02-2006 24-08-2006 75 | 87 Jonathan Montoya SARS-1 France 15-03-2003 11-04-2018 76 | 93 Allen Lee FLU-2018 Egypt 23-10-2010 13-10-2015 77 | 26 Ryan Compton H1N1 Brazil 28-08-2002 12-06-2004 78 | 73 Kimberly Marks EVD China 01-08-2000 08-07-2013 79 | 10 Sandra Stevens FLU-2018 Switzerland 19-08-2006 19-09-2006 80 | 67 Justin Perez COVID-2019 Italy 17-03-2011 24-07-2011 81 | 51 Brittany Gray MERS-COV Brazil 20-07-2012 09-07-2016 82 | 17 Adam Bennett COVID-2019 China 02-01-2002 16-04-2014 83 | 99 Heather Warren SARS-1 Denmark 15-05-2000 17-03-2013 84 | 32 Christina Mullins FLU-2018 China 13-06-2007 09-04-2009 85 | 6 Jason Hurley FLU-2018 Australia 11-06-2012 20-02-2019 86 | 9 Glen Mason SARS-1 Germany 23-05-2004 28-10-2012 87 | 59 Jessica Garcia MERS-COV Greece 27-12-2008 28-10-2012 88 | 45 Lauren Clark SARS-1 Greece 31-10-2001 25-04-2011 89 | -------------------------------------------------------------------------------- /DiseaseMonitor/modules/BalancedTree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "BalancedTree.h" 3 | #include "Patient.h" 4 | #include 5 | #include 6 | // Return the max value between 2 ints 7 | static int max(int a, int b) { 8 | return (a > b) ? a : b; 9 | } 10 | 11 | extern Pointer empty; 12 | 13 | // Create a new tree entry 14 | BalancedTreeEntry create_balanced_tree_entry(Date date, Pointer assgn) { 15 | BalancedTreeEntry entry = malloc(sizeof(*entry)); 16 | assert(entry != NULL); 17 | entry->date = date; 18 | entry->assigned_patient = assgn; 19 | 20 | return entry; 21 | } 22 | 23 | // Create a new tree node 24 | TreeNode create_tree_node(BalancedTreeEntry value) { 25 | return create_binary_node(value); 26 | } 27 | 28 | // Create an empty balanced tree 29 | BalancedTree create_balanced_tree(CompareFunc compare, DestroyFunc destroy) { 30 | return create_binary_tree(compare, destroy); 31 | } 32 | 33 | int node_height(TreeNode node) { 34 | return node != NULL ? node->height : 0; 35 | } 36 | 37 | // Update the node height 38 | void update_node_height(TreeNode node) { 39 | node->height = 1 + max(node_height(node->left), node_height(node->right)); 40 | } 41 | 42 | // Return the difference of the heights of the left & right subtree 43 | int node_balance(TreeNode node) { 44 | return node_height(node->left) - node_height(node->right); 45 | } 46 | 47 | //========Implementation of the 4 esential tree rotations=========// 48 | 49 | TreeNode right_rotation(TreeNode node) { 50 | TreeNode left = node->left; 51 | TreeNode left_right = left->right; 52 | 53 | left->right = node; 54 | node->left = left_right; 55 | 56 | update_node_height(node); 57 | update_node_height(left); 58 | 59 | return left; 60 | } 61 | 62 | TreeNode left_rotation(TreeNode node) { 63 | TreeNode right = node->right; 64 | TreeNode right_left = right->left; 65 | 66 | right->left = node; 67 | node->right = right_left; 68 | 69 | update_node_height(node); 70 | update_node_height(right); 71 | 72 | return right; 73 | } 74 | 75 | TreeNode left_right_rotation(TreeNode node) { 76 | node->left = left_rotation(node->left); 77 | return right_rotation(node); 78 | } 79 | 80 | TreeNode right_left_rotation(TreeNode node) { 81 | node->right = right_rotation(node->right); 82 | return left_rotation(node); 83 | } 84 | 85 | // Make sure that the balanced property is maintained. If not, repair it 86 | TreeNode maintain_property(TreeNode node) { 87 | update_node_height(node); 88 | int balance = node_balance(node); 89 | // If the right subtree is unbalanced 90 | if (balance < -1) { 91 | if (node_balance(node->right) <=0) { 92 | return left_rotation(node); 93 | } else { 94 | return right_left_rotation(node); 95 | } 96 | } else if (balance > 1) { // If the left subtree is unbalanced 97 | if (node_balance(node->left) >= 0) { 98 | return right_rotation(node); 99 | } else { 100 | return left_right_rotation(node); 101 | } 102 | } else { // Everything is balanced! Just return the node, no rotation needed 103 | return node; 104 | } 105 | } 106 | 107 | TreeNode insert_node_to_tree(Tree tree, TreeNode node, BalancedTreeEntry entry) { 108 | node = insert_binary_node(node, tree->compare, entry); 109 | return maintain_property(node); 110 | } 111 | 112 | // Give the root of the tree, in order to insert a new entry to it. 113 | void balanced_tree_insert(BalancedTree tree, BalancedTreeEntry value) { 114 | assert(value != NULL); 115 | tree->root = insert_node_to_tree(tree, tree->root, value); 116 | tree->size++; 117 | } 118 | 119 | // Find all the nodes in a tree that are grater than a const, and satisfy a given condition 120 | int total_nodes_grater_than(BalancedTree tree, Pointer x, ConditionFunc cond, char* cond_item) { 121 | return grater_than(tree->root, x, tree->compare, cond, cond_item); 122 | } 123 | 124 | // Recursive function to find all the nodes in a tree that are grater than a const 125 | int grater_than(TreeNode node, Pointer x, CompareFunc compare, ConditionFunc cond, char* cond_item) { 126 | int count = 0; 127 | if (node != NULL) { 128 | if (compare(x, node->value) <= 0) { 129 | count += grater_than(node->left, x, compare, cond, cond_item); 130 | if (cond == NULL) 131 | count++; 132 | else { 133 | if (cond(node->value, cond_item) == true) 134 | count++; 135 | } 136 | } 137 | count += grater_than(node->right, x, compare, cond, cond_item); 138 | } 139 | return count; 140 | } 141 | 142 | // Traverse a balanced tree, bt recursively calling node_traverse 143 | int balanced_tree_cond_traverse(BalancedTree tree, ConditionFunc cond) { 144 | return node_cond_traverse(tree->root, cond); 145 | } 146 | 147 | // Return how many nodes satisfy the condition fuction passed to the func. 148 | int node_cond_traverse(TreeNode node, ConditionFunc cond) { 149 | int count = 0; 150 | if (node != NULL) { 151 | count += node_cond_traverse(node->left, cond); 152 | if (cond(node->value, NULL) == true) { 153 | count++; 154 | } 155 | count += node_cond_traverse(node->right, cond); 156 | } 157 | return count; 158 | } 159 | 160 | // Traverse a balanced tree with a visit function 161 | void balanced_tree_traverse(BalancedTree tree, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p) { 162 | node_traverse(tree->root, visit, c, d1, d2, p); 163 | } 164 | 165 | void node_traverse(TreeNode node, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p) { 166 | if (node != NULL) { 167 | node_traverse(node->left, visit, c, d1, d2, p); 168 | visit(node->value, c, d1, d2, p); 169 | node_traverse(node->right, visit, c, d1, d2, p); 170 | } 171 | } 172 | 173 | // Destroy a balanced tree, by freeing all the memory allocated 174 | void balanced_tree_destroy(Pointer t) { 175 | BalancedTree tree = (BalancedTree)t; 176 | if (tree != empty) { 177 | destroy_node(tree->root, tree->destroy); 178 | free(tree); 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /DiseaseMonitor/modules/BinaryTree.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "BinaryTree.h" 4 | extern Pointer empty; 5 | // Create a node for a binary tree 6 | TreeNode create_binary_node(Pointer item) { 7 | TreeNode node = malloc(sizeof(*node)); 8 | assert(node != NULL); 9 | node->value = item; 10 | node->left = node->right = NULL; 11 | 12 | node->height = 0; // In this implementation we do not care about the height 13 | 14 | return node; 15 | } 16 | 17 | // Insert a node into a binary tree, specialized in __binary search__ 18 | TreeNode insert_binary_node(TreeNode node, CompareFunc compare, Pointer item) { 19 | if (node == NULL) { 20 | // Base case, if we reach a leaf, insert the entry 21 | return create_binary_node(item); 22 | } 23 | // Apply the rules of binary search 24 | if (compare(item, node->value) <= 0) { 25 | node->left = insert_binary_node(node->left, compare, item); 26 | } else { 27 | node->right = insert_binary_node(node->right, compare, item); 28 | } 29 | return node; 30 | } 31 | 32 | // Initialize the bt struct 33 | BinaryTree create_binary_tree(CompareFunc compare, DestroyFunc destroy) { 34 | BinaryTree tree = malloc(sizeof(*tree)); 35 | assert(tree != NULL); 36 | tree->root = NULL; 37 | tree->compare = compare; 38 | tree->destroy = destroy; 39 | tree->size = 0; 40 | 41 | return tree; 42 | } 43 | 44 | // Insert to the binary tree, by recursively calling the insert function, starting from the tree's root 45 | void binary_tree_insert(BinaryTree tree, Pointer item) { 46 | tree->root = insert_binary_node(tree->root, tree->compare, item); 47 | tree->size++; 48 | } 49 | 50 | // Destroy a binary tree, by applying a destroy function in ecah node 51 | void destroy_node(TreeNode node, DestroyFunc destroy) { 52 | if (node != NULL) { 53 | destroy_node(node->left, destroy); 54 | destroy_node(node->right, destroy); 55 | if (destroy != NULL) 56 | destroy(node->value); 57 | free(node->value); 58 | free(node); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DiseaseMonitor/modules/Dates.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Dates.h" 6 | 7 | bool empty_string(char* str) { 8 | if (str == NULL) 9 | return true; 10 | for (int i = 0;; i++) { 11 | if (str[i] != '\n' && str[i] != ' ' && str[i] != '\0' && str[i] != '-') 12 | return false; 13 | if (str[i] == '\0' || str[i] == '\0') 14 | return true; 15 | } 16 | } 17 | 18 | // Convert a DD-MM-YYYY string to an actual date 19 | Date string_to_date(char* d) { 20 | // We know that our string type is __identical__ to DD-MM-YYYY. 21 | // so, we are going to take advantage of it 22 | if (empty_string(d)) { 23 | Date new_date = {.day = 0, .month = 0, .year = 0}; 24 | return new_date; 25 | } 26 | char delim[3] = "-\n"; 27 | char* day = strtok(d, delim); 28 | char* month = strtok(NULL, delim); 29 | char* year = strtok(NULL, delim); 30 | assert(day != NULL && month != NULL && year!= NULL); 31 | // Update the struct fields and return it 32 | Date new_date = {.day = atoi(day), .month = atoi(month), .year = atoi(year)}; 33 | return new_date; 34 | } 35 | 36 | // Returns true if date is null (aka -) 37 | bool check_if_null_date(Date date) { 38 | return date.day == 0 ? true : false; 39 | } 40 | 41 | // Checks if a pair of dates is valid, meaning the first is before the second 42 | bool check_valid_dates(Date date1, Date date2) { 43 | return (compare_dates(date1, date2) < 0) ? true : false; 44 | } 45 | 46 | // Comparing 2 dates. Return -1 if 1st is prior(or euqal) to 2nd, or 1 otherwise 47 | int compare_dates(Date date1, Date date2) { 48 | if (date1.year < date2.year) 49 | return -1; 50 | else if (date1.year > date2.year) 51 | return 1; 52 | else { 53 | if (date1.month < date2.month) 54 | return -1; 55 | else if (date1.month > date2.month) 56 | return 1; 57 | else { 58 | if (date1.day <= date2.day) 59 | return -1; 60 | else 61 | return 1; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /DiseaseMonitor/modules/HashTable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "HashTable.h" 4 | #include 5 | 6 | // Special pointer to indicate an empty entry 7 | Pointer empty = ∅ 8 | 9 | HashEntry create_hash_entry(char* key, Pointer item) { 10 | HashEntry new_entry = malloc(sizeof(*new_entry)); 11 | assert(new_entry != NULL); 12 | new_entry->key = key; 13 | new_entry->item = item; 14 | 15 | return new_entry; 16 | } 17 | 18 | // Create a node for the hash table, given its bucket size 19 | HashNode create_hash_node(int n_buckets) { 20 | HashNode new_node = malloc(sizeof(*new_node)); 21 | assert(new_node != NULL); 22 | new_node->bucket = malloc(n_buckets * sizeof(HashEntry)); 23 | // initialize our entries to null 24 | for (int j = 0; j < n_buckets; j++) { 25 | new_node->bucket[j] = create_hash_entry(empty, empty); 26 | } 27 | // We are going to use that as an overflow list pointer for each bucket 28 | new_node->next = NULL; 29 | 30 | return new_node; 31 | } 32 | 33 | // Create an empty hash table, with pre-determined size 34 | HashTable hash_create(int size, HashFunc hash_fn, int bucket_size, DestroyFunc destroy) { 35 | HashTable ht = malloc(sizeof(*ht)); 36 | assert(ht != NULL); 37 | assert(size >= 0); 38 | // Hold the bucketsize, so that is a multiple of the size of the struct. 39 | ht->bucket_size = bucket_size; 40 | ht->bucket_max_entries = (bucket_size - sizeof(Pointer)) / sizeof(struct hash_entry); 41 | if (bucket_size / sizeof(struct hash_entry) < 1) { 42 | printf("Fatal error, bucketsize too small. Exiting the monitor\n"); 43 | exit(EXIT_FAILURE); 44 | } 45 | // allocate space for our array 46 | ht->array = malloc(size * sizeof(HashNode)); 47 | // create the first nodes of each bucket's overflow list 48 | for (int i = 0; i < size; i++) { 49 | ht->array[i] = create_hash_node(ht->bucket_max_entries); 50 | } 51 | // Store the size of the ht 52 | ht->size = size; 53 | // Initially the ht is empty 54 | ht->items = 0; 55 | // We deferentiate the hts, by passing each hash & destroy function into the struct 56 | ht->hash_function = hash_fn; 57 | ht->destroy_items = destroy; 58 | return ht; 59 | } 60 | 61 | // Insert a new entry in the hash table 62 | void hash_insert(HashTable ht, char* key, Pointer item) { 63 | // Use the hash function to determine where our new entry is gonna go 64 | int hash_id = ht->hash_function(key) % ht->size; 65 | int pos; 66 | bool found_empty = false; 67 | HashNode requested = ht->array[hash_id]; 68 | // forever running loop until we find an empty space 69 | while (true) { 70 | for (pos = 0; pos < ht->bucket_max_entries; pos++) { 71 | // if we reach an empty entry, break our loop 72 | if (requested->bucket[pos]->item == empty) { 73 | found_empty = true; 74 | break; 75 | } 76 | } 77 | if (found_empty) 78 | break; 79 | // if we do not have an other element in our overflow list 80 | if (requested->next == NULL) { 81 | // create a new one 82 | HashNode new_node = create_hash_node(ht->bucket_max_entries); 83 | // and assign it to our node 84 | requested->next = new_node; 85 | requested = new_node; 86 | } else { 87 | // else, just proceed to the next member of the overflow list 88 | requested = requested->next; 89 | } 90 | } 91 | // Insert the new entry in the correct position 92 | requested->bucket[pos]->item = item; 93 | requested->bucket[pos]->key = key; 94 | // increase the ht items by one 95 | ht->items++; 96 | } 97 | // Search for a specific entry in the ht, given the key. If search fails, null is returned 98 | HashEntry hash_search(HashTable ht, char* key) { 99 | // Use the hash function to determine where our new entry is gonna go 100 | int hash_id = ht->hash_function(key) % ht->size; 101 | HashNode requested = ht->array[hash_id]; 102 | // run the loop until there are no more elements in the bucket's overflow list 103 | while (requested != NULL) { 104 | // traverse all the entries in the hash node, trying to find the desired one 105 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 106 | // if the entry has something in it, proceed, else we did not find what we wanted 107 | if (requested->bucket[pos]->item != empty) { 108 | if (strcmp(requested->bucket[pos]->key, key) == 0) 109 | return requested->bucket[pos]; 110 | } else { 111 | return NULL; 112 | } 113 | } 114 | // proceed to the next member of the overflow list 115 | requested = requested->next; 116 | } 117 | // the entry was not found, so return null 118 | return NULL; 119 | } 120 | 121 | // Traverse the ht, by applying a visit function to each entry 122 | void hash_traverse(HashTable ht, VisitFunc visit, Pointer d1, Pointer d2, Pointer p) { 123 | // traverse all the buckets in our hash table 124 | for (int i = 0; i < ht->size; i++) { 125 | HashNode current = ht->array[i]; 126 | // run the loop until there are no more elements in the bucket's overflow list 127 | while (current != NULL) { 128 | // traverse all the entries in the hash node 129 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 130 | // if the item is not empty, apply the visit function 131 | if (current->bucket[pos]->item != empty) { 132 | char* str = current->bucket[pos]->key; 133 | visit(current->bucket[pos], d1, d2, str, p); 134 | } 135 | } 136 | // proceed to the next member of the overflow list 137 | current = current->next; 138 | } 139 | } 140 | } 141 | 142 | // Free the memory allocated by the hash table by destroying the ht 143 | void hash_destroy(HashTable ht) { 144 | // we want to free all the buckets 145 | for (int i = 0; i < ht->size; i++) { 146 | HashNode current = ht->array[i]; 147 | // traverse the overflow list 148 | while (current != NULL) { 149 | HashNode next_node = current->next; 150 | // destroy all the entries in the node 151 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 152 | if (current->bucket[pos]->item != empty) 153 | ht->destroy_items(current->bucket[pos]->item); 154 | free(current->bucket[pos]); 155 | } 156 | // free the pointers to the bucket, and the bucket itself 157 | free(current->bucket); 158 | free(current); 159 | // proceed to the next member of the overflow list 160 | current = next_node; 161 | } 162 | } 163 | // free the array, and the hash table it self 164 | free(ht->array); 165 | free(ht); 166 | } -------------------------------------------------------------------------------- /DiseaseMonitor/modules/HeapUsingCBT.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "HeapUsingCBT.h" 3 | #include 4 | 5 | 6 | 7 | //============= Complete binary tree functions ================// 8 | 9 | // We are going to use those function in order to create a heap 10 | 11 | CBTree create_CBTree(DestroyFunc destroy) { 12 | CBTree tree = malloc(sizeof(*tree)); 13 | tree->root = NULL; 14 | tree->compare = NULL; 15 | tree->destroy = destroy; 16 | tree->size = 0; 17 | 18 | return tree; 19 | } 20 | 21 | CBTreeNode create_cbinary_node(Pointer item) { 22 | CBTreeNode node = malloc(sizeof(*node)); 23 | node->value = item; 24 | node->left = node->right = node->parent = NULL; 25 | 26 | return node; 27 | } 28 | 29 | // Given a integer, return the node in the correct position of that tree 30 | CBTreeNode get_node_by_index (CBTree tree, int i) { 31 | // basecase: if we want the 1st item, then it's in the root 32 | if (i == 1){ 33 | return tree->root; 34 | } 35 | // else, we want to call recursively the function to reach the childs 36 | // all the even numbers are right childs 37 | else if (i % 2 == 0) { 38 | CBTreeNode node = get_node_by_index(tree, i / 2); 39 | return node->left; 40 | } 41 | // all the odd numbers are right childs 42 | else { 43 | CBTreeNode node = get_node_by_index(tree, i / 2); 44 | return node->right; 45 | } 46 | } 47 | 48 | // Insert a new entry into a complete binary tree 49 | void CBTree_insert(CBTree tree, BTEntry entry) { 50 | int pos = tree->size + 1; 51 | // Same logic with the previous function 52 | if (pos == 1) { 53 | tree->root = create_cbinary_node(entry); 54 | } 55 | else if (pos % 2 == 0) { 56 | CBTreeNode node = get_node_by_index(tree, pos / 2); 57 | node->left = create_cbinary_node(entry); 58 | node->left->parent = node; 59 | } 60 | else { 61 | CBTreeNode node = get_node_by_index(tree, pos / 2); 62 | node->right = create_cbinary_node(entry); 63 | node->right->parent = node; 64 | } 65 | tree->size++; 66 | } 67 | 68 | // remove the last entry from a cbt 69 | BTEntry remove_last(CBTree tree) { 70 | int pos = tree->size; 71 | assert (pos > 0); 72 | CBTreeNode node = get_node_by_index(tree, pos); 73 | if (node->parent != NULL) { 74 | if (pos % 2 == 0) { 75 | node->parent->left = NULL; 76 | } else { 77 | node->parent->right = NULL; 78 | } 79 | } 80 | BTEntry to_return = node->value; 81 | free(node); 82 | tree->size--; 83 | return to_return; 84 | } 85 | 86 | //============= Binary heap functions =========================// 87 | 88 | // Create a heap entry 89 | HeapEntry create_heap_entry(char* key, int priority) { 90 | HeapEntry new_entry = malloc(sizeof(*new_entry)); 91 | new_entry->key = key; 92 | new_entry->priority = priority; 93 | 94 | return new_entry; 95 | } 96 | 97 | // Create a heap, simply by creating a complete bt 98 | Heap create_heap(DestroyFunc destroy) { 99 | return create_CBTree(destroy); 100 | } 101 | 102 | void heapify_up(Heap heap, HeapNode node) { 103 | // if the root is null, there is nothing to do 104 | if (heap->root == NULL) 105 | return; 106 | HeapNode parent; 107 | // we continue in this loop while the child has bigger priority than the parent 108 | // thus we need to swap them 109 | while ((parent = node->parent) != NULL) { 110 | HeapEntry parent_entry = parent->value; 111 | HeapEntry node_entry = node->value; 112 | // done! the heap property is restored! 113 | if (parent_entry->priority >= node_entry->priority) 114 | return; 115 | // performing the neccesary swapping 116 | parent->value = node_entry; 117 | node->value = parent_entry; 118 | // we move up one level 119 | node = parent; 120 | } 121 | } 122 | 123 | void heapify_down(Heap heap, HeapNode node) { 124 | // Nothing to be done if the node that we want to begin the heapify from is null! 125 | if (node == NULL) 126 | return; 127 | HeapNode child; 128 | // we continue in this loop while the child has bigger priority than the parent 129 | // thus we need to swap them 130 | while ((child = node->left) != NULL) { 131 | // we naively choose the left child to compare. gonna change that very soon... 132 | HeapEntry child_entry = child->value; 133 | HeapEntry node_entry = node->value; 134 | 135 | // The right node might have bigger priority than the left, so we check that (here, we've changed our naive assumption) 136 | HeapNode right = node->right; 137 | if ((right != NULL) && right->value->priority > child->value->priority) { 138 | child = right; 139 | child_entry = right->value; 140 | } 141 | // done! the heap priority is restored! 142 | if (node->value->priority >= child->value->priority) 143 | return; 144 | // performing the neccesary swapping 145 | node->value = child_entry; 146 | child->value = node_entry; 147 | // we move down one level 148 | node = child; 149 | } 150 | } 151 | 152 | // Insert a new (key, priority) pair into the heap 153 | void heap_insert(Heap heap, int priority, char* key) { 154 | // Create the entry 155 | HeapEntry entry = create_heap_entry(key, priority); 156 | // insert it as the last item in the heap 157 | CBTree_insert(heap, entry); 158 | // heapify so the heap priority is preserved 159 | heapify_up(heap, get_node_by_index(heap, heap->size)); 160 | } 161 | 162 | HeapNode get_nth_node(Heap heap, int i) { 163 | return get_node_by_index(heap, i); 164 | } 165 | 166 | // Pop the item of the heap with the biggest priority 167 | HeapEntry pop(Heap heap) { 168 | // Get access to the root of the heap 169 | HeapNode root = heap->root; 170 | assert(root != NULL); 171 | // Hold the value we want to return 172 | HeapEntry to_return = root->value; 173 | // remove the last value of the heap, in order to place it in the root 174 | HeapEntry last = remove_last(heap); 175 | // if there are still items in the heap 176 | if (heap->size != 0) { 177 | // assign the last item into the root 178 | root->value = last; 179 | // heapify so the heap priority is preserved 180 | heapify_down(heap, heap->root); 181 | } 182 | // return the item with the biggest priority that we've collected 183 | return to_return; 184 | } 185 | 186 | void destroy_heap(Heap heap) { 187 | while (heap->size != 0) { 188 | HeapEntry entry = pop(heap); 189 | if (heap->destroy != NULL) 190 | heap->destroy(entry); 191 | free(entry); 192 | } 193 | free(heap); 194 | } -------------------------------------------------------------------------------- /DiseaseMonitor/modules/Patient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Patient.h" 5 | #include "Dates.h" 6 | 7 | extern Pointer empty; 8 | // Given a string, create a new patient record 9 | Patient* create_patient(char* str) { 10 | Patient* p = malloc(sizeof(*p)); 11 | assert(p != NULL); 12 | // Our delimiter will be the space character 13 | char delim[3] = " \n"; 14 | 15 | char* id = strdup(strtok(str, delim)); 16 | char* first_name = strdup(strtok(NULL, delim)); 17 | char* last_name = strdup(strtok(NULL, delim)); 18 | char* disease = strdup(strtok(NULL, delim)); 19 | char* country = strdup(strtok(NULL, delim)); 20 | char* entry_date = strdup(strtok(NULL, delim)); 21 | char* check_exit = strtok(NULL, delim); 22 | if (id == NULL || first_name == NULL || last_name == NULL 23 | || disease == NULL || country == NULL || entry_date == NULL) { 24 | free(p); 25 | return NULL; 26 | } 27 | Date entry = string_to_date(entry_date); 28 | Date exitd; 29 | if (check_exit) { 30 | char* exit_date = strdup(check_exit); 31 | exitd = string_to_date(exit_date); 32 | free(exit_date); 33 | } else { 34 | exitd = string_to_date(NULL); 35 | } 36 | if (!check_if_null_date(exitd) && !check_valid_dates(entry, exitd)) 37 | return NULL; 38 | p->id = id; 39 | p->first_name = first_name; 40 | p->last_name = last_name; 41 | p->disease = disease; 42 | p->country = country; 43 | p->entry_date = entry; 44 | p->exit_date = exitd; 45 | // free the temp strings allocated 46 | free(entry_date); 47 | // Return the desired struct 48 | return p; 49 | } 50 | 51 | // Destroy a patient, in order to free the allocated memory 52 | void destroy_patient(Pointer p) { 53 | Patient* patient = (Patient*)p; 54 | if (patient != empty && patient != NULL) { 55 | free (patient->id); 56 | free(patient->first_name); 57 | free(patient->last_name); 58 | free(patient->disease); 59 | free(patient->country); 60 | 61 | free(patient); 62 | } 63 | } -------------------------------------------------------------------------------- /DiseaseMonitor/programs/menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Dates.h" 6 | #include "Queries.h" 7 | 8 | void monitor_menu() { 9 | char input[STRING_SIZE]; 10 | char delim[3] = " \n"; 11 | printf("Enter a new query\n"); 12 | while (fgets(input, STRING_SIZE, stdin) != NULL) { 13 | char* instruction = strtok(input, delim); 14 | char* info = strtok(NULL, "\n"); 15 | if (!strcmp(instruction, "/globalDiseaseStats")) { 16 | globalDiseaseStats(info); 17 | } 18 | else if(!strcmp(instruction, "/diseaseFrequency")) { 19 | diseaseFrequency(info); 20 | } 21 | else if(!strcmp(instruction, "/topk-Diseases")) { 22 | topk_Diseases(info); 23 | } 24 | else if(!strcmp(instruction, "/topk-Countries")) { 25 | topk_Countries(info); 26 | } 27 | else if(!strcmp(instruction, "/insertPatientRecord")) { 28 | insertPatientRecord(info); 29 | } 30 | else if(!strcmp(instruction, "/recordPatientExit")) { 31 | recordPatientExit(info); 32 | } 33 | else if(!strcmp(instruction, "/numCurrentPatients")) { 34 | numCurrentPatients(info); 35 | } 36 | else if(!strcmp(instruction, "/exit")) { 37 | exit_monitor(); 38 | return; 39 | } 40 | else { 41 | printf("Query not recognized. Choose one of the default options\n"); 42 | } 43 | printf("\nEnter a new query\n"); 44 | } 45 | } -------------------------------------------------------------------------------- /DiseaseMonitor/programs/monitor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "GlobalStructures.h" 6 | 7 | int main(int argc, char const *argv[]) { 8 | int bucket_size; 9 | char* in_file; 10 | if (argc < 9 || argc > 9) { 11 | printf("Use as ./diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries –h2 countryHashtableNumOfEntries –b bucketSize\n"); 12 | exit(EXIT_FAILURE); 13 | } 14 | if (! strcmp(argv[1], "-p")) { 15 | in_file = strdup(argv[2]); 16 | } else { 17 | printf("Use as ./diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries –h2 countryHashtableNumOfEntries –b bucketSize\n"); 18 | exit(EXIT_FAILURE); 19 | } 20 | if (! strcmp(argv[3], "-h1")) { 21 | num_diseases = atoi(argv[4]); 22 | } else { 23 | printf("Use as ./diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries –h2 countryHashtableNumOfEntries –b bucketSize\n"); 24 | exit(EXIT_FAILURE); 25 | } 26 | if (! strcmp(argv[5], "-h2")) { 27 | num_countries = atoi(argv[6]); 28 | } else { 29 | printf("Use as ./diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries –h2 countryHashtableNumOfEntries –b bucketSize\n"); 30 | exit(EXIT_FAILURE); 31 | } 32 | if (! strcmp(argv[7], "-b")) { 33 | bucket_size = atoi(argv[8]); 34 | } else { 35 | printf("Use as ./diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries –h2 countryHashtableNumOfEntries –b bucketSize\n"); 36 | exit(EXIT_FAILURE); 37 | } 38 | parse_input(in_file, bucket_size); 39 | monitor_menu(); 40 | 41 | free(in_file); 42 | exit(EXIT_SUCCESS); 43 | } 44 | -------------------------------------------------------------------------------- /DiseaseMonitor/programs/parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "GlobalStructures.h" 6 | 7 | 8 | int compare(Pointer first, Pointer second) { 9 | TreeEntry entry1 = (TreeEntry)first; 10 | TreeEntry entry2 = (TreeEntry)second; 11 | return compare_dates(entry1->date, entry2->date); 12 | } 13 | 14 | uint hash_strings(void* key) { 15 | char* str = (char*)key; 16 | int h = 0, a = 33; 17 | for (; *str != '\0'; str++) 18 | h = (a * h + *str); 19 | return h; 20 | } 21 | 22 | // Count how many lines there are in a file 23 | int nlines(FILE* input) { 24 | int n_lines = 0; 25 | while(!feof(input)) { 26 | char ch = fgetc(input); 27 | if(ch == '\n') { 28 | n_lines++; 29 | } 30 | } 31 | rewind(input); 32 | return n_lines; 33 | } 34 | 35 | void parse_input (char* file, int bucket_size){ 36 | // Initialize the hash tables, with destroy functions to delete the trees, as the items are going to be bsts 37 | printf("Collecting the data from the input file...\n"); 38 | diseaseHashTable = hash_create(num_diseases, hash_strings, bucket_size, balanced_tree_destroy); 39 | countryHashTable = hash_create(num_countries, hash_strings, bucket_size, balanced_tree_destroy); 40 | // Open the input file 41 | FILE* input = fopen(file, "r"); 42 | if (input == NULL) { 43 | printf("Input file not found\n"); 44 | exit(EXIT_FAILURE); 45 | } 46 | lines = nlines(input); 47 | // We are going to store all the pointers to the strings that we are given, in order to 48 | // free them afterwards 49 | // all_strings_from_file = malloc(lines * sizeof(char*)); 50 | // We are going to use one extra hashtable, in order to quickly search if the patient allready exists 51 | // We are going to pass destroy patient as a destroy func, in order to free all the memory occupied 52 | // by the patients when we are done. 53 | patients = hash_create(lines / 10 + 1, hash_strings, bucket_size, destroy_patient); 54 | char* str; 55 | for (int i = 0; i < lines; i++) { 56 | str = malloc(STRING_SIZE); 57 | fgets(str, STRING_SIZE, input); 58 | // store the string 59 | // all_strings_from_file[i] = str; 60 | // Create a new patient record 61 | Patient* p = create_patient(str); 62 | if (p == NULL) { 63 | printf("Fatal error. Wrong patient data. Check the %dth line of your input\n", i); 64 | exit_monitor(); 65 | free(str); 66 | exit(EXIT_FAILURE); 67 | } 68 | // The key for the balanced tree will be tha patient's entry date to the hospital 69 | Date tree_key = p->entry_date; 70 | HashEntry disease_search_result = hash_search(diseaseHashTable, p->disease); 71 | HashEntry country_search_result = hash_search(countryHashTable, p->country); 72 | // If we find the entry in the hash table, then we update 73 | // its tree, by inserting the new patient 74 | if(country_search_result != NULL) { 75 | BalancedTreeEntry new_tree_entry = create_balanced_tree_entry(tree_key, p); 76 | BalancedTree result_tree = country_search_result->item; 77 | balanced_tree_insert(result_tree, new_tree_entry); 78 | } 79 | // If we do not find the entry, then we insert it, with a tree with only one node as a key 80 | else { 81 | // Create a new tree and store its 1st date 82 | // Attention: We pass NULL as destroy func, because we do not want the patients, neither 83 | // the dates to be freed, cause we have 2 trees possibly pointing in the same node 84 | BalancedTree result_tree = create_balanced_tree(compare, NULL); 85 | hash_insert(countryHashTable, p->country, result_tree); 86 | BalancedTreeEntry new_tree_entry = create_balanced_tree_entry(tree_key, p); 87 | balanced_tree_insert(result_tree, new_tree_entry); 88 | 89 | } 90 | // Same thing about the diseases hash table 91 | if(disease_search_result != NULL) { 92 | BalancedTreeEntry new_tree_entry = create_balanced_tree_entry(tree_key, p); 93 | BalancedTree result_tree = disease_search_result->item; 94 | balanced_tree_insert(result_tree, new_tree_entry); 95 | } 96 | // If we do not find the entry, then we insert it, with an empty tree as a key 97 | else { 98 | BalancedTree result_tree = create_balanced_tree(compare, NULL); 99 | hash_insert(diseaseHashTable, p->disease, result_tree); 100 | BalancedTreeEntry new_tree_entry = create_balanced_tree_entry(tree_key, p); 101 | balanced_tree_insert(result_tree, new_tree_entry); 102 | } 103 | // Search for the patient in the patients ht. If a patient with the same id is found 104 | // terminate the program. Else, just insert the pointer to the patient record in the hash. 105 | HashEntry patient_entry = hash_search(patients, p->id); 106 | if (patient_entry == NULL) { 107 | hash_insert(patients, p->id, p); 108 | } else { 109 | printf("Fatal error. Patient with id %s already exists. Terminating the monitor\n", p->id); 110 | exit_monitor(); 111 | free(str); 112 | exit(EXIT_FAILURE); 113 | } 114 | free(str); 115 | } 116 | fclose(input); 117 | } 118 | -------------------------------------------------------------------------------- /DiseaseServer/create_programs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd master 4 | make 5 | cd ../whoServer 6 | make 7 | cd ../whoClient 8 | make 9 | cd ../worker 10 | make 11 | cd .. -------------------------------------------------------------------------------- /DiseaseServer/include/BalancedTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BinaryTree.h" 3 | #include "Dates.h" 4 | 5 | 6 | // Our entries to our bst will be of this form 7 | struct tree_entry { 8 | Date date; 9 | Pointer assigned_patient; 10 | }; 11 | typedef struct tree_entry* TreeEntry; 12 | 13 | typedef BinaryTree BalancedTree; 14 | typedef TreeEntry BalancedTreeEntry; 15 | 16 | BalancedTreeEntry create_balanced_tree_entry(Date date, Pointer assgn); 17 | TreeNode create_tree_node(BalancedTreeEntry value); 18 | BalancedTree create_balanced_tree(CompareFunc compare, DestroyFunc destroy); 19 | void balanced_tree_insert(BalancedTree tree, BalancedTreeEntry value); 20 | int total_nodes_grater_than(BalancedTree tree, Pointer x, ConditionFunc cond, char* cond_item); 21 | int grater_than(TreeNode node, Pointer x, CompareFunc compare, ConditionFunc cond, char* cond_item); 22 | int balanced_tree_cond_traverse(Pointer tree, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2); 23 | int node_cond_traverse(TreeNode node, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2); 24 | void balanced_tree_traverse(BalancedTree tree, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p); 25 | void node_traverse(TreeNode node, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p); 26 | void balanced_tree_destroy(Pointer tree); 27 | void destroy_node(TreeNode node, DestroyFunc destroy); 28 | -------------------------------------------------------------------------------- /DiseaseServer/include/BinaryTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TreeTypes.h" 4 | 5 | typedef struct bs_tree_node* TreeNode; 6 | 7 | struct bs_tree_node { 8 | TreeNode left, right; 9 | Pointer value; 10 | uint height; 11 | }; 12 | 13 | typedef Tree BinaryTree; 14 | 15 | TreeNode create_binary_node(Pointer item); 16 | TreeNode insert_binary_node(TreeNode node, CompareFunc compare, Pointer item); 17 | BinaryTree create_binary_tree(CompareFunc compare, DestroyFunc destroy); 18 | void binary_tree_insert(BinaryTree tree, Pointer item); 19 | void destroy_node(TreeNode node, DestroyFunc destroy); -------------------------------------------------------------------------------- /DiseaseServer/include/CompareFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | #include "BalancedTree.h" 4 | #include "Dates.h" 5 | 6 | int compare(Pointer first, Pointer second) { 7 | TreeEntry entry1 = (TreeEntry)first; 8 | TreeEntry entry2 = (TreeEntry)second; 9 | return compare_dates(entry1->date, entry2->date); 10 | } -------------------------------------------------------------------------------- /DiseaseServer/include/Dates.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | typedef struct date { 6 | int day; 7 | int month; 8 | int year; 9 | } Date; 10 | 11 | #define EOF_DATE (Date)0 12 | 13 | bool empty_string(char* str); 14 | Date string_to_date(char* d); 15 | char* date_to_string(Date date); 16 | int compare_dates(Date date1, Date date2); 17 | bool check_valid_dates(Date date1, Date date2); 18 | bool check_if_null_date(Date date); 19 | bool check_equal_dates(Date date1, Date date2); -------------------------------------------------------------------------------- /DiseaseServer/include/GlobalStructures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Includes that must be made 4 | #include "common_types.h" 5 | #include "Patient.h" 6 | #include "Dates.h" 7 | #include "HashTable.h" 8 | #include "BalancedTree.h" 9 | #include "HeapUsingCBT.h" 10 | #include "Queries.h" 11 | 12 | // Our global pointers to stroe some sizes 13 | int num_diseases, num_countries; 14 | 15 | // Our global pointers to store the hash tables 16 | HashTable diseaseHashTable; 17 | HashTable countryHashTable; 18 | HashTable patients; 19 | 20 | // number of lines in the file opened 21 | int lines; -------------------------------------------------------------------------------- /DiseaseServer/include/HashTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | 4 | typedef uint (*HashFunc)(Pointer); 5 | 6 | struct hash_entry { 7 | char* key; 8 | Pointer item; 9 | }; 10 | 11 | typedef struct hash_entry* HashEntry; 12 | 13 | typedef struct hash_node* HashNode; 14 | 15 | struct hash_node { 16 | HashEntry* bucket; 17 | HashNode next; 18 | }; 19 | 20 | 21 | 22 | #define HASH_EOF (HashNode)0 23 | 24 | struct hash_table { 25 | HashNode* array; 26 | int size; 27 | int items; 28 | int bucket_size; 29 | int bucket_max_entries; 30 | HashFunc hash_function; 31 | DestroyFunc destroy_items; 32 | }; 33 | 34 | typedef struct hash_table* HashTable; 35 | 36 | HashEntry create_hash_entry(char* key, Pointer item); 37 | HashTable hash_create(int size, HashFunc hash_fn, int bucket_size, DestroyFunc destroy); 38 | void hash_insert(HashTable ht, char* key, Pointer item); 39 | HashEntry hash_search(HashTable ht, char* name); 40 | void hash_update(HashTable ht, char* key, Pointer new_item); 41 | void hash_traverse(HashTable ht, VisitFunc print, Pointer d1, Pointer d2, Pointer p); 42 | void hash_destroy(HashTable ht); -------------------------------------------------------------------------------- /DiseaseServer/include/HeapUsingCBT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TreeTypes.h" 4 | 5 | struct binary_tree_entry { 6 | char* key; 7 | int priority; 8 | }; 9 | 10 | typedef struct binary_tree_entry* BTEntry; 11 | typedef struct binary_tree_node* BTNode; 12 | 13 | struct binary_tree_node { 14 | BTNode left, right, parent; 15 | BTEntry value; 16 | }; 17 | // A Complete binary tree node, is just a node 18 | typedef BTNode CBTreeNode; 19 | 20 | // As well as a complete binary tree 21 | typedef Tree CBTree; 22 | 23 | CBTree create_CBTree(DestroyFunc destroy); 24 | CBTreeNode create_cbinary_node(Pointer item); 25 | CBTreeNode get_node_by_index (CBTree tree, int i); 26 | void CBTree_insert(CBTree tree, BTEntry entry); 27 | // void CBTree_destroy(Pointer t); 28 | 29 | // Our heap is just a complete binary tree 30 | typedef CBTree Heap; 31 | 32 | typedef BTEntry HeapEntry; 33 | typedef CBTreeNode HeapNode; 34 | 35 | HeapEntry create_heap_entry(char* key, int priority); 36 | Heap create_heap(DestroyFunc destroy); 37 | void heapify_up(Heap heap, BTNode node); 38 | void heapify_down(Heap heap, BTNode node); 39 | void heap_insert(Heap heap, int priority, char* key); 40 | HeapNode get_nth_node(Heap heap, int i); 41 | HeapEntry pop(Heap heap); 42 | void destroy_heap(Heap heap); -------------------------------------------------------------------------------- /DiseaseServer/include/LinkedList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | typedef struct list_node* ListNode; 6 | typedef struct list* List; 7 | 8 | 9 | struct list_node { 10 | Pointer value; 11 | ListNode next; 12 | }; 13 | 14 | struct list { 15 | ListNode head; 16 | int size; 17 | DestroyFunc destroy; 18 | CompareFunc compare; 19 | }; 20 | 21 | 22 | List create_list(CompareFunc compare, DestroyFunc destroy); 23 | void list_insert(List list, Pointer node); 24 | bool list_search(List list, Pointer value); 25 | bool in_list(List list, Pointer node_info); 26 | int list_size(List list); 27 | bool is_empty(List list); 28 | Pointer list_nth(List list, int n); 29 | void destroy_list(Pointer list); 30 | -------------------------------------------------------------------------------- /DiseaseServer/include/Patient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common_types.h" 3 | #include "Dates.h" 4 | 5 | typedef struct patient { 6 | char* id; 7 | char* first_name; 8 | char* last_name; 9 | char* disease; 10 | char* country; 11 | int age; 12 | Date entry_date; 13 | Date exit_date; 14 | } Patient; 15 | 16 | Patient* create_patient(char* str, char* country, char* entry_date); 17 | void destroy_patient(Pointer p); -------------------------------------------------------------------------------- /DiseaseServer/include/Queries.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | #include "common_functions.h" 5 | #include "HashTable.h" 6 | #include "BalancedTree.h" 7 | #include "HashTable.h" 8 | #include "Dates.h" 9 | #include "Patient.h" 10 | #include "HeapUsingCBT.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | bool recordPatientExit(char* info, HashTable patients, char* exit_d); 17 | int disease_frequency(char* virus, char* arg2, char* arg3, char* country, HashTable diseases_hash); 18 | char* search_patient_record(char* r_id, HashTable patients); 19 | int num_patient_admissions(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash); 20 | int num_patient_discharges(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash); 21 | void topk_age_ranges(int k, char* country, char* disease, char* day1, char* day2, HashTable diseases_hash, int write); 22 | -------------------------------------------------------------------------------- /DiseaseServer/include/TreeTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_types.h" 4 | 5 | // Our nodes have 2 child nodes, their value, and ther height in the tree 6 | // in order for it to remain balanced 7 | 8 | struct tree { 9 | Pointer root; 10 | int size; 11 | CompareFunc compare; 12 | DestroyFunc destroy; 13 | }; 14 | 15 | typedef struct tree* Tree; -------------------------------------------------------------------------------- /DiseaseServer/include/WorkerMenu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "HashTable.h" 4 | #include "LinkedList.h" 5 | #include "Queries.h" 6 | 7 | bool worker_menu(char* qu, List dirs, HashTable patients, HashTable diseases_hash, int writing); -------------------------------------------------------------------------------- /DiseaseServer/include/buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_functions.h" 4 | 5 | struct buffer{ 6 | int* data; 7 | int start; 8 | int end; 9 | int size; 10 | int count; 11 | }; 12 | 13 | typedef struct buffer* Buffer; 14 | 15 | Buffer initialize_buffer(int size); 16 | void place(Buffer buffer, int data); 17 | int obtain (Buffer buffer); 18 | void destroy_buffer(Buffer buff); 19 | -------------------------------------------------------------------------------- /DiseaseServer/include/common_functions.h: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "BalancedTree.h" 3 | #include "HashTable.h" 4 | #include "LinkedList.h" 5 | 6 | int compare_strings (Pointer a, Pointer b); 7 | int compare_ints (Pointer a, Pointer b); 8 | int compare(Pointer first, Pointer second); 9 | uint hash_strings(void* key); 10 | int nlines(FILE* input); 11 | int n_words(char* str); 12 | char* nth_word(char* s, int n); 13 | char* read_from_pipe(int fd, int buff_size); 14 | char* concat(char* str1, char* str2); 15 | char* itoa(int n); 16 | void write_to_pipe(int fd, int buff_size, char* info); 17 | char* read_from_socket(int fd); 18 | int write_to_socket(int fd, void* buff, int len); 19 | void print_list_contents(Pointer ent, Pointer d1, Pointer d2, Pointer d3, Pointer d4); 20 | void print_countries(Pointer ent, Pointer d1, Pointer d2, Pointer d3, Pointer d4); 21 | int get_pos_from_pid(int pid, int* workers, int n_workers); 22 | int n_files_in_worker(char* path, List countries); -------------------------------------------------------------------------------- /DiseaseServer/include/common_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define perror2(s, e) fprintf(stderr, "%s: %s\n", s, strerror(e)) 10 | #define STRING_SIZE 1024 11 | #define HASH_SIZE 100 12 | #define BUCKET_SIZE 100 13 | #define FAILED -1 14 | #define PERMS 0777 15 | #define MAX_ALIVE_CONNECTIONS 10 16 | // Usefull typedefs 17 | typedef void* Pointer; 18 | 19 | typedef unsigned int uint; 20 | 21 | typedef int (*CompareFunc)(Pointer a, Pointer b); 22 | typedef void (*DestroyFunc)(Pointer value); 23 | typedef uint (*HashFunc)(Pointer); 24 | typedef void (*VisitFunc)(Pointer, Pointer, Pointer, Pointer, Pointer); 25 | typedef bool (*ConditionFunc)(Pointer, Pointer, Pointer, Pointer); 26 | 27 | 28 | void parse_input (char* file, int bucket_size); 29 | void monitor_menu(); -------------------------------------------------------------------------------- /DiseaseServer/include/stats.h: -------------------------------------------------------------------------------- 1 | void print_stats(int active_workers, int buff_size, int* reading); -------------------------------------------------------------------------------- /DiseaseServer/master/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = master 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build 6 | SRC_DIRS = . ../modules 7 | INC_DIRS = ../include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name "*.c") 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ -lpthread 17 | 18 | # c source 19 | $(BUILD_DIR)/%.c.o: %.c 20 | $(MKDIR_P) $(dir $@) 21 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 22 | 23 | 24 | .PHONY: clean 25 | 26 | clean: 27 | $(RM) -r $(BUILD_DIR) 28 | $(RM) $(TARGET_EXEC) 29 | 30 | -include $(DEPS) 31 | 32 | MKDIR_P ?= mkdir -p -------------------------------------------------------------------------------- /DiseaseServer/master/master_main.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | 3 | void operation(int n_workers, int buff_size, char* input_dir, char* server_ip, char* server_port); 4 | 5 | int main(int argc, char* argv[]) { 6 | int n_workers, buffer_size; 7 | char* input_dir, *server_ip, *server_port; 8 | // check the arguments given 9 | if (argc != 11) { 10 | fprintf(stderr, "Usage: ./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir\n"); 11 | exit(EXIT_FAILURE); 12 | } 13 | if (! strcmp(argv[1], "-w")) { 14 | n_workers = atoi(argv[2]); 15 | } else { 16 | fprintf(stderr, "Usage: ./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir\n"); 17 | exit(EXIT_FAILURE); 18 | } 19 | if (! strcmp(argv[3], "-b")) { 20 | buffer_size = atoi(argv[4]); 21 | } else { 22 | fprintf(stderr, "Usage: ./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir\n"); 23 | exit(EXIT_FAILURE); 24 | } 25 | if (! strcmp(argv[5], "-s")) { 26 | server_ip = strdup(argv[6]); 27 | } else { 28 | fprintf(stderr, "Usage: ./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir\n"); 29 | exit(EXIT_FAILURE); 30 | } 31 | if (! strcmp(argv[7], "-p")) { 32 | server_port = strdup(argv[8]); 33 | } else { 34 | fprintf(stderr, "Usage: ./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir\n"); 35 | exit(EXIT_FAILURE); 36 | } 37 | if (! strcmp(argv[9], "-i")) { 38 | input_dir = strdup(argv[10]); 39 | } else { 40 | fprintf(stderr, "Usage: ./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir\n"); 41 | exit(EXIT_FAILURE); 42 | } 43 | operation(n_workers, buffer_size, input_dir, server_ip, server_port); 44 | // free those in order to not have any leaks 45 | free(input_dir); 46 | free(server_ip); 47 | free(server_port); 48 | // everything is sound! 49 | exit(EXIT_SUCCESS); 50 | } -------------------------------------------------------------------------------- /DiseaseServer/misc/commands.txt: -------------------------------------------------------------------------------- 1 | /diseaseFrequency disease date1 date2 [country] 2 | /topk-AgeRanges k country disease date1 date2 3 | /searchPatientRecord recordID 4 | /numPatientAdmissions disease date1 date2 [country] 5 | /numPatientDischarges disease date1 date2 [country] -------------------------------------------------------------------------------- /DiseaseServer/misc/countries.txt: -------------------------------------------------------------------------------- 1 | Afghanistan 2 | Albania 3 | Algeria 4 | Andorra 5 | Angola 6 | Argentina 7 | Armenia 8 | Australia 9 | Austria 10 | Azerbaijan 11 | Bahamas 12 | Bahrain 13 | Bangladesh 14 | Barbados 15 | Belarus 16 | Belgium 17 | Belize 18 | Benin 19 | Colombia 20 | Comoros 21 | Congo 22 | Croatia 23 | Cuba 24 | Cyprus 25 | Denmark 26 | Djibouti 27 | Dominica 28 | Ecuador 29 | Egypt 30 | Eritrea 31 | Estonia 32 | Ethiopia 33 | Fiji 34 | Finland 35 | France 36 | Gabon 37 | Gambia 38 | Georgia 39 | Germany 40 | Ghana 41 | Greece 42 | Grenada 43 | Guatemala 44 | Guinea 45 | Guyana 46 | Haiti 47 | Honduras 48 | Liberia 49 | Libya 50 | Liechtenstein 51 | Lithuania 52 | Tanzania 53 | Thailand 54 | Togo 55 | Tonga 56 | Tuvalu 57 | Uganda 58 | Ukraine 59 | USA 60 | UK 61 | -------------------------------------------------------------------------------- /DiseaseServer/misc/diseases.txt: -------------------------------------------------------------------------------- 1 | COVID-19 2 | H1N1 3 | FLU-2018 4 | SARS-1 5 | MERS 6 | HIV 7 | SARS-COV-2 8 | -------------------------------------------------------------------------------- /DiseaseServer/misc/queries.txt: -------------------------------------------------------------------------------- 1 | /diseaseFrequency COVID-19 05-11-2019 24-05-2020 France 2 | /numPatientAdmissions H1N1 28-05-2019 10-01-2020 3 | /diseaseFrequency H1N1 02-06-2020 23-07-2020 4 | /diseaseFrequency SARS-1 09-12-2019 19-05-2020 Bahrain 5 | /searchPatientRecord 876 6 | /numPatientDischarges H1N1 06-06-2020 09-08-2020 7 | /searchPatientRecord 332 8 | /numPatientAdmissions COVID-19 23-08-2020 09-10-2020 Guatemala 9 | /numPatientAdmissions HIV 15-09-2019 27-06-2020 10 | /topk-AgeRanges 2 Colombia SARS-COV-2 12-10-2019 26-08-2020 11 | /numPatientAdmissions H1N1 11-01-2020 18-07-2020 12 | /searchPatientRecord 481 13 | /diseaseFrequency HIV 20-01-2019 23-05-2020 14 | /searchPatientRecord 167 15 | /numPatientDischarges FLU-2018 19-04-2020 05-09-2020 Belgium 16 | /numPatientDischarges FLU-2018 14-09-2019 19-06-2020 17 | /topk-AgeRanges 2 Gabon MERS 03-05-2019 03-03-2020 18 | /numPatientAdmissions SARS-COV-2 28-08-2020 05-10-2020 Fiji 19 | /searchPatientRecord 98 20 | /diseaseFrequency MERS 29-10-2019 24-12-2019 -------------------------------------------------------------------------------- /DiseaseServer/modules/BalancedTree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "BalancedTree.h" 3 | #include "Patient.h" 4 | #include 5 | #include 6 | 7 | extern Pointer empty; 8 | 9 | // Create a new tree entry 10 | BalancedTreeEntry create_balanced_tree_entry(Date date, Pointer assgn) { 11 | BalancedTreeEntry entry = malloc(sizeof(*entry)); 12 | assert(entry != NULL); 13 | entry->date = date; 14 | entry->assigned_patient = assgn; 15 | 16 | return entry; 17 | } 18 | 19 | // Create a new tree node 20 | TreeNode create_tree_node(BalancedTreeEntry value) { 21 | return create_binary_node(value); 22 | } 23 | 24 | // Create an empty balanced tree 25 | BalancedTree create_balanced_tree(CompareFunc compare, DestroyFunc destroy) { 26 | return create_binary_tree(compare, destroy); 27 | } 28 | 29 | // insert a node to the dree 30 | TreeNode insert_node_to_tree(Tree tree, TreeNode node, BalancedTreeEntry entry) { 31 | return insert_binary_node(node, tree->compare, entry); 32 | } 33 | 34 | // Give the root of the tree, in order to insert a new entry to it. 35 | void balanced_tree_insert(BalancedTree tree, BalancedTreeEntry value) { 36 | assert(value != NULL); 37 | tree->root = insert_node_to_tree(tree, tree->root, value); 38 | tree->size++; 39 | } 40 | 41 | // Find all the nodes in a tree that are grater than a const, and satisfy a given condition 42 | int total_nodes_grater_than(BalancedTree tree, Pointer x, ConditionFunc cond, char* cond_item) { 43 | return grater_than(tree->root, x, tree->compare, cond, cond_item); 44 | } 45 | 46 | // Recursive function to find all the nodes in a tree that are grater than a const 47 | int grater_than(TreeNode node, Pointer x, CompareFunc compare, ConditionFunc cond, char* cond_item) { 48 | int count = 0; 49 | if (node != NULL) { 50 | if (compare(x, node->value) <= 0) { 51 | count += grater_than(node->left, x, compare, cond, cond_item); 52 | if (cond == NULL) 53 | count++; 54 | else { 55 | if (cond(node->value, cond_item, NULL, NULL) == true) 56 | count++; 57 | } 58 | } 59 | count += grater_than(node->right, x, compare, cond, cond_item); 60 | } 61 | return count; 62 | } 63 | 64 | // Traverse a balanced tree, bt recursively calling node_traverse 65 | int balanced_tree_cond_traverse(Pointer t, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2) { 66 | BalancedTree tree = (BalancedTree)t; 67 | return node_cond_traverse(tree->root, cond, a, d1, d2); 68 | } 69 | 70 | // Return how many nodes satisfy the condition fuction passed to the func. 71 | int node_cond_traverse(TreeNode node, ConditionFunc cond, Pointer a, Pointer d1, Pointer d2) { 72 | int count = 0; 73 | if (node != NULL) { 74 | count += node_cond_traverse(node->left, cond, a, d1, d2); 75 | if (cond(node->value, a, d1, d2) == true) { 76 | count++; 77 | } 78 | count += node_cond_traverse(node->right, cond, a, d1, d2); 79 | } 80 | return count; 81 | } 82 | 83 | // Traverse a balanced tree with a visit function 84 | void balanced_tree_traverse(BalancedTree tree, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p) { 85 | node_traverse(tree->root, visit, c, d1, d2, p); 86 | } 87 | 88 | void node_traverse(TreeNode node, VisitFunc visit, Pointer c, Pointer d1, Pointer d2, Pointer p) { 89 | if (node != NULL) { 90 | node_traverse(node->left, visit, c, d1, d2, p); 91 | visit(node->value, c, d1, d2, p); 92 | node_traverse(node->right, visit, c, d1, d2, p); 93 | } 94 | } 95 | 96 | // Destroy a balanced tree, by freeing all the memory allocated 97 | void balanced_tree_destroy(Pointer t) { 98 | BalancedTree tree = (BalancedTree)t; 99 | if (tree != empty) { 100 | destroy_node(tree->root, tree->destroy); 101 | free(tree); 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /DiseaseServer/modules/BinaryTree.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "BinaryTree.h" 4 | extern Pointer empty; 5 | // Create a node for a binary tree 6 | TreeNode create_binary_node(Pointer item) { 7 | TreeNode node = malloc(sizeof(*node)); 8 | assert(node != NULL); 9 | node->value = item; 10 | node->left = node->right = NULL; 11 | 12 | node->height = 0; // In this implementation we do not care about the height 13 | 14 | return node; 15 | } 16 | 17 | // Insert a node into a binary tree, specialized in __binary search__ 18 | TreeNode insert_binary_node(TreeNode node, CompareFunc compare, Pointer item) { 19 | if (node == NULL) { 20 | // Base case, if we reach a leaf, insert the entry 21 | return create_binary_node(item); 22 | } 23 | // Apply the rules of binary search 24 | if (compare(item, node->value) <= 0) { 25 | node->left = insert_binary_node(node->left, compare, item); 26 | } else { 27 | node->right = insert_binary_node(node->right, compare, item); 28 | } 29 | return node; 30 | } 31 | 32 | // Initialize the bt struct 33 | BinaryTree create_binary_tree(CompareFunc compare, DestroyFunc destroy) { 34 | BinaryTree tree = malloc(sizeof(*tree)); 35 | assert(tree != NULL); 36 | tree->root = NULL; 37 | tree->compare = compare; 38 | tree->destroy = destroy; 39 | tree->size = 0; 40 | 41 | return tree; 42 | } 43 | 44 | // Insert to the binary tree, by recursively calling the insert function, starting from the tree's root 45 | void binary_tree_insert(BinaryTree tree, Pointer item) { 46 | tree->root = insert_binary_node(tree->root, tree->compare, item); 47 | tree->size++; 48 | } 49 | 50 | // Destroy a binary tree, by applying a destroy function in ecah node 51 | void destroy_node(TreeNode node, DestroyFunc destroy) { 52 | if (node != NULL) { 53 | destroy_node(node->left, destroy); 54 | destroy_node(node->right, destroy); 55 | if (destroy != NULL) 56 | destroy(node->value); 57 | free(node->value); 58 | free(node); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DiseaseServer/modules/Dates.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Dates.h" 6 | 7 | bool empty_string(char* str) { 8 | if (str == NULL) 9 | return true; 10 | for (int i = 0;; i++) { 11 | if (str[i] != '\n' && str[i] != ' ' && str[i] != '\0' && str[i] != '-') 12 | return false; 13 | if (str[i] == '\0' || str[i] == '\0') 14 | return true; 15 | } 16 | } 17 | 18 | // Convert a DD-MM-YYYY string to an actual date 19 | Date string_to_date(char* d) { 20 | // We know that our string type is __identical__ to DD-MM-YYYY. 21 | // so, we are going to take advantage of it 22 | if (empty_string(d)) { 23 | Date new_date = {.day = 0, .month = 0, .year = 0}; 24 | return new_date; 25 | } 26 | char delim[3] = "-\n"; 27 | char* day = strtok(d, delim); 28 | char* month = strtok(NULL, delim); 29 | char* year = strtok(NULL, delim); 30 | assert(day != NULL && month != NULL && year!= NULL); 31 | // Update the struct fields and return it 32 | Date new_date = {.day = atoi(day), .month = atoi(month), .year = atoi(year)}; 33 | return new_date; 34 | } 35 | 36 | char* date_to_string(Date date) { 37 | if (check_if_null_date(date)) { 38 | return "--"; 39 | } 40 | // take advantage of the date format 41 | char* res = malloc(11 * sizeof(*res)); 42 | snprintf(res, 11, "%d-%d-%d", date.day, date.month, date.year); 43 | return res; 44 | } 45 | 46 | // Returns true if date is null (aka -) 47 | bool check_if_null_date(Date date) { 48 | return date.day == 0 ? true : false; 49 | } 50 | 51 | // Checks if a pair of dates is valid, meaning the first is before the second 52 | bool check_valid_dates(Date date1, Date date2) { 53 | return (compare_dates(date1, date2) < 0) ? true : false; 54 | } 55 | 56 | // Comparing 2 dates. Return -1 if 1st is prior(or euqal) to 2nd, or 1 otherwise 57 | int compare_dates(Date date1, Date date2) { 58 | if (date1.year < date2.year) 59 | return -1; 60 | else if (date1.year > date2.year) 61 | return 1; 62 | else { 63 | if (date1.month < date2.month) 64 | return -1; 65 | else if (date1.month > date2.month) 66 | return 1; 67 | else { 68 | if (date1.day <= date2.day) 69 | return -1; 70 | else 71 | return 1; 72 | } 73 | } 74 | } 75 | 76 | bool check_equal_dates(Date date1, Date date2) { 77 | return (date1.day == date2.day && date1.month == date2.month 78 | && date1.year == date2.year); 79 | } -------------------------------------------------------------------------------- /DiseaseServer/modules/HashTable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "HashTable.h" 4 | #include 5 | 6 | // Special pointer to indicate an empty entry 7 | Pointer empty = ∅ 8 | 9 | HashEntry create_hash_entry(char* key, Pointer item) { 10 | HashEntry new_entry = malloc(sizeof(*new_entry)); 11 | assert(new_entry != NULL); 12 | new_entry->key = key; 13 | new_entry->item = item; 14 | 15 | return new_entry; 16 | } 17 | 18 | // Create a node for the hash table, given its bucket size 19 | HashNode create_hash_node(int n_buckets) { 20 | HashNode new_node = malloc(sizeof(*new_node)); 21 | assert(new_node != NULL); 22 | new_node->bucket = malloc(n_buckets * sizeof(HashEntry)); 23 | // initialize our entries to null 24 | for (int j = 0; j < n_buckets; j++) { 25 | new_node->bucket[j] = create_hash_entry(empty, empty); 26 | } 27 | // We are going to use that as an overflow list pointer for each bucket 28 | new_node->next = NULL; 29 | 30 | return new_node; 31 | } 32 | 33 | // Create an empty hash table, with pre-determined size 34 | HashTable hash_create(int size, HashFunc hash_fn, int bucket_size, DestroyFunc destroy) { 35 | HashTable ht = malloc(sizeof(*ht)); 36 | assert(ht != NULL); 37 | assert(size >= 0); 38 | // Hold the bucketsize, so that is a multiple of the size of the struct. 39 | ht->bucket_size = bucket_size; 40 | ht->bucket_max_entries = (bucket_size - sizeof(Pointer)) / sizeof(struct hash_entry); 41 | if (bucket_size / sizeof(struct hash_entry) < 1) { 42 | printf("Fatal error, bucketsize too small. Exiting the monitor\n"); 43 | exit(EXIT_FAILURE); 44 | } 45 | // allocate space for our array 46 | ht->array = malloc(size * sizeof(HashNode)); 47 | // create the first nodes of each bucket's overflow list 48 | for (int i = 0; i < size; i++) { 49 | ht->array[i] = create_hash_node(ht->bucket_max_entries); 50 | } 51 | // Store the size of the ht 52 | ht->size = size; 53 | // Initially the ht is empty 54 | ht->items = 0; 55 | // We deferentiate the hts, by passing each hash & destroy function into the struct 56 | ht->hash_function = hash_fn; 57 | ht->destroy_items = destroy; 58 | return ht; 59 | } 60 | 61 | // Insert a new entry in the hash table 62 | void hash_insert(HashTable ht, char* key, Pointer item) { 63 | // Use the hash function to determine where our new entry is gonna go 64 | int hash_id = ht->hash_function(key) % ht->size; 65 | int pos; 66 | bool found_empty = false; 67 | HashNode requested = ht->array[hash_id]; 68 | // forever running loop until we find an empty space 69 | while (true) { 70 | for (pos = 0; pos < ht->bucket_max_entries; pos++) { 71 | // if we reach an empty entry, break our loop 72 | if (requested->bucket[pos]->item == empty) { 73 | found_empty = true; 74 | break; 75 | } 76 | } 77 | if (found_empty) 78 | break; 79 | // if we do not have an other element in our overflow list 80 | if (requested->next == NULL) { 81 | // create a new one 82 | HashNode new_node = create_hash_node(ht->bucket_max_entries); 83 | // and assign it to our node 84 | requested->next = new_node; 85 | requested = new_node; 86 | } else { 87 | // else, just proceed to the next member of the overflow list 88 | requested = requested->next; 89 | } 90 | } 91 | // Insert the new entry in the correct position 92 | requested->bucket[pos]->item = item; 93 | requested->bucket[pos]->key = key; 94 | // increase the ht items by one 95 | ht->items++; 96 | } 97 | // Search for a specific entry in the ht, given the key. If search fails, null is returned 98 | HashEntry hash_search(HashTable ht, char* key) { 99 | // Use the hash function to determine where our new entry is gonna go 100 | int hash_id = ht->hash_function(key) % ht->size; 101 | HashNode requested = ht->array[hash_id]; 102 | // run the loop until there are no more elements in the bucket's overflow list 103 | while (requested != NULL) { 104 | // traverse all the entries in the hash node, trying to find the desired one 105 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 106 | // if the entry has something in it, proceed, else we did not find what we wanted 107 | if (requested->bucket[pos]->item != empty) { 108 | if (strcmp(requested->bucket[pos]->key, key) == 0) 109 | return requested->bucket[pos]; 110 | } else { 111 | return NULL; 112 | } 113 | } 114 | // proceed to the next member of the overflow list 115 | requested = requested->next; 116 | } 117 | // the entry was not found, so return null 118 | return NULL; 119 | } 120 | 121 | // Update a hash entry providing a new item 122 | void hash_update(HashTable ht, char* key, Pointer new_item) { 123 | HashEntry entry = hash_search(ht, key); 124 | if (entry) { 125 | entry->item = new_item; //TODO: Maybe free the old one 126 | } else { 127 | hash_insert(ht, key, new_item); 128 | } 129 | } 130 | // Traverse the ht, by applying a visit function to each entry 131 | void hash_traverse(HashTable ht, VisitFunc visit, Pointer d1, Pointer d2, Pointer p) { 132 | // traverse all the buckets in our hash table 133 | for (int i = 0; i < ht->size; i++) { 134 | HashNode current = ht->array[i]; 135 | // run the loop until there are no more elements in the bucket's overflow list 136 | while (current != NULL) { 137 | // traverse all the entries in the hash node 138 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 139 | // if the item is not empty, apply the visit function 140 | if (current->bucket[pos]->item != empty) { 141 | char* str = current->bucket[pos]->key; 142 | visit(current->bucket[pos], d1, d2, str, p); 143 | } 144 | } 145 | // proceed to the next member of the overflow list 146 | current = current->next; 147 | } 148 | } 149 | } 150 | 151 | // Free the memory allocated by the hash table by destroying the ht 152 | void hash_destroy(HashTable ht) { 153 | // we want to free all the buckets 154 | for (int i = 0; i < ht->size; i++) { 155 | HashNode current = ht->array[i]; 156 | // traverse the overflow list 157 | while (current != NULL) { 158 | HashNode next_node = current->next; 159 | // destroy all the entries in the node 160 | for (int pos = 0; pos < ht->bucket_max_entries; pos++) { 161 | if (current->bucket[pos]->item != empty && ht->destroy_items != NULL) 162 | ht->destroy_items(current->bucket[pos]->item); 163 | free(current->bucket[pos]); 164 | } 165 | // free the pointers to the bucket, and the bucket itself 166 | free(current->bucket); 167 | free(current); 168 | // proceed to the next member of the overflow list 169 | current = next_node; 170 | } 171 | } 172 | // free the array, and the hash table it self 173 | free(ht->array); 174 | free(ht); 175 | } -------------------------------------------------------------------------------- /DiseaseServer/modules/HeapUsingCBT.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "HeapUsingCBT.h" 3 | #include 4 | #include 5 | 6 | 7 | //============= Complete binary tree functions ================// 8 | 9 | // We are going to use those function in order to create a heap 10 | 11 | CBTree create_CBTree(DestroyFunc destroy) { 12 | CBTree tree = malloc(sizeof(*tree)); 13 | tree->root = NULL; 14 | tree->compare = NULL; 15 | tree->destroy = destroy; 16 | tree->size = 0; 17 | 18 | return tree; 19 | } 20 | 21 | CBTreeNode create_cbinary_node(Pointer item) { 22 | CBTreeNode node = malloc(sizeof(*node)); 23 | node->value = item; 24 | node->left = node->right = node->parent = NULL; 25 | 26 | return node; 27 | } 28 | 29 | // Given a integer, return the node in the correct position of that tree 30 | CBTreeNode get_node_by_index (CBTree tree, int i) { 31 | // basecase: if we want the 1st item, then it's in the root 32 | if (i == 1){ 33 | return tree->root; 34 | } 35 | // else, we want to call recursively the function to reach the childs 36 | // all the even numbers are right childs 37 | else if (i % 2 == 0) { 38 | CBTreeNode node = get_node_by_index(tree, i / 2); 39 | return node->left; 40 | } 41 | // all the odd numbers are right childs 42 | else { 43 | CBTreeNode node = get_node_by_index(tree, i / 2); 44 | return node->right; 45 | } 46 | } 47 | 48 | // Insert a new entry into a complete binary tree 49 | void CBTree_insert(CBTree tree, BTEntry entry) { 50 | int pos = tree->size + 1; 51 | // Same logic with the previous function 52 | if (pos == 1) { 53 | tree->root = create_cbinary_node(entry); 54 | } 55 | else if (pos % 2 == 0) { 56 | CBTreeNode node = get_node_by_index(tree, pos / 2); 57 | node->left = create_cbinary_node(entry); 58 | node->left->parent = node; 59 | } 60 | else { 61 | CBTreeNode node = get_node_by_index(tree, pos / 2); 62 | node->right = create_cbinary_node(entry); 63 | node->right->parent = node; 64 | } 65 | tree->size++; 66 | } 67 | 68 | // remove the last entry from a cbt 69 | BTEntry remove_last(CBTree tree) { 70 | int pos = tree->size; 71 | assert (pos > 0); 72 | CBTreeNode node = get_node_by_index(tree, pos); 73 | if (node->parent != NULL) { 74 | if (pos % 2 == 0) { 75 | node->parent->left = NULL; 76 | } else { 77 | node->parent->right = NULL; 78 | } 79 | } 80 | BTEntry to_return = node->value; 81 | free(node); 82 | tree->size--; 83 | return to_return; 84 | } 85 | 86 | //============= Binary heap functions =========================// 87 | 88 | // Create a heap entry 89 | HeapEntry create_heap_entry(char* key, int priority) { 90 | HeapEntry new_entry = malloc(sizeof(*new_entry)); 91 | new_entry->key = key; 92 | new_entry->priority = priority; 93 | 94 | return new_entry; 95 | } 96 | 97 | // Create a heap, simply by creating a complete bt 98 | Heap create_heap(DestroyFunc destroy) { 99 | return create_CBTree(destroy); 100 | } 101 | 102 | void heapify_up(Heap heap, HeapNode node) { 103 | // if the root is null, there is nothing to do 104 | if (heap->root == NULL) 105 | return; 106 | HeapNode parent; 107 | // we continue in this loop while the child has bigger priority than the parent 108 | // thus we need to swap them 109 | while ((parent = node->parent) != NULL) { 110 | HeapEntry parent_entry = parent->value; 111 | HeapEntry node_entry = node->value; 112 | // done! the heap property is restored! 113 | if (parent_entry->priority > node_entry->priority) 114 | return; 115 | else if (parent_entry->priority == node_entry->priority) { 116 | if (strcmp(parent_entry->key, node_entry->key) < 0) 117 | return; 118 | } 119 | // performing the neccesary swapping 120 | parent->value = node_entry; 121 | node->value = parent_entry; 122 | // we move up one level 123 | node = parent; 124 | } 125 | } 126 | 127 | void heapify_down(Heap heap, HeapNode node) { 128 | // Nothing to be done if the node that we want to begin the heapify from is null! 129 | if (node == NULL) 130 | return; 131 | HeapNode child; 132 | // we continue in this loop while the child has bigger priority than the parent 133 | // thus we need to swap them 134 | while ((child = node->left) != NULL) { 135 | // we naively choose the left child to compare. gonna change that very soon... 136 | HeapEntry child_entry = child->value; 137 | HeapEntry node_entry = node->value; 138 | 139 | // The right node might have bigger priority than the left, so we check that (here, we've changed our naive assumption) 140 | HeapNode right = node->right; 141 | if ((right != NULL) && right->value->priority > child->value->priority) { 142 | child = right; 143 | child_entry = right->value; 144 | } else if ((right != NULL) && (right->value->priority == child->value->priority) && strcmp(right->value->key, child->value->key) < 0) { 145 | child = right; 146 | child_entry = right->value; 147 | } 148 | // done! the heap priority is restored! 149 | if (node->value->priority > child->value->priority) 150 | return; 151 | else if ((node->value->priority == child->value->priority) && strcmp(node->value->key, child->value->key) < 0) 152 | return; 153 | // performing the neccesary swapping 154 | node->value = child_entry; 155 | child->value = node_entry; 156 | // we move down one level 157 | node = child; 158 | } 159 | } 160 | 161 | // Insert a new (key, priority) pair into the heap 162 | void heap_insert(Heap heap, int priority, char* key) { 163 | // Create the entry 164 | HeapEntry entry = create_heap_entry(key, priority); 165 | // insert it as the last item in the heap 166 | CBTree_insert(heap, entry); 167 | // heapify so the heap priority is preserved 168 | heapify_up(heap, get_node_by_index(heap, heap->size)); 169 | } 170 | 171 | HeapNode get_nth_node(Heap heap, int i) { 172 | return get_node_by_index(heap, i); 173 | } 174 | 175 | // Pop the item of the heap with the biggest priority 176 | HeapEntry pop(Heap heap) { 177 | // Get access to the root of the heap 178 | HeapNode root = heap->root; 179 | assert(root != NULL); 180 | // Hold the value we want to return 181 | HeapEntry to_return = root->value; 182 | // remove the last value of the heap, in order to place it in the root 183 | HeapEntry last = remove_last(heap); 184 | // if there are still items in the heap 185 | if (heap->size != 0) { 186 | // assign the last item into the root 187 | root->value = last; 188 | // heapify so the heap priority is preserved 189 | heapify_down(heap, heap->root); 190 | } 191 | // return the item with the biggest priority that we've collected 192 | return to_return; 193 | } 194 | 195 | void destroy_heap(Heap heap) { 196 | while (heap->size != 0) { 197 | HeapEntry entry = pop(heap); 198 | if (heap->destroy != NULL) 199 | heap->destroy(entry); 200 | free(entry); 201 | } 202 | free(heap); 203 | } -------------------------------------------------------------------------------- /DiseaseServer/modules/LinkedList.c: -------------------------------------------------------------------------------- 1 | #include "../include/LinkedList.h" 2 | 3 | // Create and initialize a new list 4 | List create_list(CompareFunc compare, DestroyFunc destroy) { 5 | List new_list = malloc(sizeof(*new_list)); 6 | new_list->head = NULL; 7 | new_list->size = 0; 8 | new_list->compare = compare; 9 | new_list->destroy = destroy; 10 | 11 | return new_list; 12 | } 13 | 14 | // Insert an item in the end of a list 15 | void list_insert(List list, Pointer node_info) { 16 | // Special behavior if a list is empty 17 | if (list->size == 0) { 18 | list->head = malloc(sizeof(*list->head)); 19 | list->head->value = node_info; 20 | list->head->next = NULL; 21 | list->size++; 22 | return; 23 | } 24 | ListNode temp = list->head; 25 | while ( temp->next != NULL) { 26 | temp = temp->next; 27 | } 28 | 29 | ListNode new_node = malloc(sizeof(*new_node)); 30 | new_node->value = node_info; 31 | new_node->next = NULL; 32 | temp->next = new_node; 33 | list->size++; 34 | } 35 | 36 | // Search for a specific entry into the list 37 | bool list_search(List list, Pointer node_info) { 38 | ListNode temp = list->head; 39 | 40 | while (temp != NULL) { 41 | if (list->compare(temp->value, node_info) == 0) { 42 | return true; 43 | } 44 | temp = temp->next; 45 | } 46 | return false; 47 | } 48 | 49 | // Function to see if an item exists in a list 50 | bool in_list(List list, Pointer node_info) { 51 | return list_search(list, node_info); 52 | } 53 | 54 | // Return the size of the list 55 | int list_size(List list) { 56 | return list->size; 57 | } 58 | 59 | // Return the nth item of a list 60 | Pointer list_nth(List list, int n) { 61 | ListNode temp = list->head; 62 | while (temp) { 63 | if (n == 0) { 64 | return temp->value; 65 | } 66 | temp = temp->next; n--; 67 | } 68 | // If we reach this point, then the item is not on the list, thus return NULL 69 | return NULL; 70 | } 71 | 72 | // check if a list is empty 73 | bool is_empty(List list) { 74 | return (list->size == 0); 75 | } 76 | // Destroy a list by freeing all of the memory occupied 77 | void destroy_list(Pointer l) { 78 | List list = (List)l; 79 | ListNode temp = list->head; 80 | while (temp != NULL) { 81 | ListNode next = temp->next; 82 | if (list->destroy) { 83 | list->destroy(temp->value); 84 | } 85 | free(temp); 86 | temp = next; 87 | } 88 | free(list); 89 | } 90 | -------------------------------------------------------------------------------- /DiseaseServer/modules/Patient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Patient.h" 5 | #include "Dates.h" 6 | 7 | extern Pointer empty; 8 | // Given a string, create a new patient record 9 | Patient* create_patient(char* str, char* country, char* entry_str) { 10 | char* entry_date = strdup(entry_str); 11 | Patient* p = malloc(sizeof(*p)); 12 | assert(p != NULL); 13 | // Our delimiter will be the space character 14 | char delim[3] = " \n"; 15 | char* id = strdup(strtok(str, delim)); 16 | char* action = strdup(strtok(NULL, delim)); 17 | // just ignore the action, we've checked it earlier 18 | free(action); 19 | char* first_name = strdup(strtok(NULL, delim)); 20 | char* last_name = strdup(strtok(NULL, delim)); 21 | char* disease = strdup(strtok(NULL, delim)); 22 | int age = atoi(strtok(NULL, delim)); 23 | if (id == NULL || first_name == NULL || last_name == NULL 24 | || disease == NULL || country == NULL || entry_date == NULL || 25 | age < 0 || age > 120) { 26 | free(p); 27 | return NULL; 28 | } 29 | Date entry = string_to_date(entry_date); 30 | free(entry_date); 31 | p->id = id; 32 | p->first_name = first_name; 33 | p->last_name = last_name; 34 | p->disease = disease; 35 | p->country = country; 36 | p->age = age; 37 | p->entry_date = entry; 38 | // the exit day is null for the time being 39 | p->exit_date = string_to_date("-"); 40 | // Return the desired struct 41 | return p; 42 | } 43 | 44 | // Destroy a patient, in order to free the allocated memory 45 | void destroy_patient(Pointer p) { 46 | Patient* patient = (Patient*)p; 47 | if (patient != empty && patient != NULL) { 48 | free (patient->id); 49 | free(patient->first_name); 50 | free(patient->last_name); 51 | free(patient->disease); 52 | free(patient->country); 53 | 54 | free(patient); 55 | } 56 | } -------------------------------------------------------------------------------- /DiseaseServer/modules/common_functions.c: -------------------------------------------------------------------------------- 1 | #include "common_functions.h" 2 | #include 3 | #include 4 | 5 | // compare function for strings 6 | int compare_strings (Pointer a, Pointer b) { 7 | return strcmp((char*) a, (char*)b); 8 | } 9 | 10 | // compare function for ints 11 | int compare_ints(Pointer a, Pointer b) { 12 | return *(int*)a - *(int*)b; 13 | } 14 | 15 | // compare function for our entries 16 | int compare(Pointer first, Pointer second) { 17 | TreeEntry entry1 = (TreeEntry)first; 18 | TreeEntry entry2 = (TreeEntry)second; 19 | return compare_dates(entry1->date, entry2->date); 20 | } 21 | 22 | // remake of the concat function because c is useless 23 | char* concat(char* str1, char* str2) { 24 | char* res = malloc(strlen(str1) + strlen(str2) + 10); 25 | memcpy(res, str1, strlen(str1)); 26 | memcpy(res + strlen(str1), str2, strlen(str2) + 1); 27 | return res; 28 | } 29 | 30 | // Remake of the itoa function, because gcc sucks 31 | char* itoa(int n) { 32 | char* res = malloc(10); 33 | snprintf(res, 10,"%d", n); 34 | return res; 35 | } 36 | 37 | // djb2 hash function, simple and quick 38 | uint hash_strings(Pointer value) { 39 | uint hash = 5381; 40 | for (char* s = value; *s != '\0'; s++) 41 | hash = (hash << 5) + hash + *s; 42 | return hash; 43 | } 44 | 45 | // Count how many lines there are in a file 46 | int nlines(FILE* input) { 47 | int n_lines = 0; 48 | while (!feof(input)) { 49 | char ch = fgetc(input); 50 | if (ch == '\n') { 51 | n_lines++; 52 | } 53 | } 54 | rewind(input); 55 | return n_lines; 56 | } 57 | 58 | // Get the number of the words in a string 59 | int n_words(char* s) { 60 | char delim[3] = " \n"; 61 | int n = 0; 62 | char* str = strdup(s); 63 | char* res = strtok(str, delim); 64 | // read until we reach null 65 | while (res) { 66 | n++; 67 | res = strtok(NULL, delim); 68 | } 69 | // no leaks here 70 | free (str); 71 | return n; 72 | } 73 | 74 | // Get the n-th word of a string 75 | char* nth_word(char* s, int n) { 76 | char* str = strdup(s); 77 | int nw = n_words(str); 78 | char* arr[nw]; 79 | char delim[3] = " \n"; 80 | arr[0] = strtok(str, delim); 81 | int i = 1; 82 | while (i < n) { 83 | arr[i] = strtok(NULL, delim); 84 | i++; 85 | } 86 | char* ret = strdup(arr[i-1]); 87 | free(str); 88 | return ret; 89 | } 90 | 91 | // Read a string from a pipe 92 | char* read_from_pipe(int fd, int buff_size) { 93 | // find out how many bytes we want to read 94 | int n_bytes; 95 | int n = read(fd, &n_bytes, sizeof(int)); 96 | // if read fails due to an interupt, then just return null, we'll handle it later 97 | if (n == -1 && (errno == EINTR)) { 98 | return NULL; 99 | } 100 | // Allocate a string to return (space for \0 is taken into account by the write function) 101 | char* info = malloc((n_bytes + 1) * sizeof(*info)); 102 | // set how many times we must read from the buffer given the buff_size 103 | int times = (n_bytes / buff_size) + 1; 104 | for (int i = 0; i < times; i++) { 105 | // the first n-1 times, read the full message 106 | if (i < times -1) { 107 | char* current = info + (i * buff_size); 108 | read(fd, current, buff_size); 109 | } else { 110 | // the last time, read the remainder of the bytes in the file (if it is grater than 0) 111 | int remainder = n_bytes % buff_size; 112 | if (remainder) { 113 | char* current = info + (i * buff_size); 114 | read(fd, current, remainder); 115 | } 116 | } 117 | } 118 | // last byte of the new string must be \0 119 | info[n_bytes] = '\0'; 120 | // finally, return the whole message 121 | return info; 122 | } 123 | 124 | // Write a sting to the pipe, by writing buff_size bytes each time 125 | void write_to_pipe(int fd, int buff_size, char* info) { 126 | // exit violently if a buffsize of 0 is given 127 | assert(buff_size > 0); 128 | // Hold the number of the bytes in the message, so the reader will read easily 129 | int n_bytes = strlen(info); 130 | // Write the n of bytes in front of the message 131 | write(fd, &n_bytes, sizeof(int)); 132 | // set how many times we must write to the buffer given the buff_size 133 | int times = (strlen(info) / buff_size) + 1; 134 | for (int i = 0; i < times; i++) { 135 | // the first n-1 times, write the full message 136 | if (i < times - 1) { 137 | char* current = info + (i * buff_size); 138 | write(fd, current, buff_size); 139 | } 140 | else { 141 | // the last time, write the remainder of the bytes in the file (if it is grater than 0) 142 | int remainder = n_bytes % buff_size; 143 | if (remainder) { 144 | char* current = info + (i * buff_size); 145 | write(fd, current, (remainder)); 146 | } 147 | } 148 | } 149 | } 150 | 151 | // read a message from a socket, based on our communication protocol 152 | char* read_from_socket(int fd) { 153 | int len = 0; 154 | // read the length of the message 155 | read(fd, &len, sizeof(int)); 156 | // allocate space for our message 157 | char* res = malloc((len + 1) * sizeof(*res)); 158 | // read the message from the socket 159 | read(fd, res, len); 160 | res[len] = '\0'; 161 | return res; 162 | } 163 | 164 | int write_to_socket(int fd, void* buff, int len) { 165 | // write the length of the message to the socket 166 | write(fd, &len, sizeof(int)); 167 | // write the message to the socket 168 | return write(fd, buff, len); 169 | } 170 | 171 | // Function to traverse our dirs_to_workers hash table 172 | void print_list_contents(Pointer ent, Pointer d1, Pointer d2, Pointer d3, Pointer d4) { 173 | HashEntry entry = (HashEntry)ent; 174 | if (entry) { 175 | printf("%15s %d\n", entry->key, *(int*)entry->item); 176 | } 177 | } 178 | // Function to traverse our dirs_to_workers hash table 179 | void print_countries(Pointer ent, Pointer f, Pointer d2, Pointer d3, Pointer d4) { 180 | FILE* file = (FILE*)f; 181 | HashEntry entry = (HashEntry)ent; 182 | if (entry) { 183 | fprintf(file, "%15s\n", entry->key); 184 | } 185 | } 186 | 187 | // Assign a pid to a workers's id 188 | int get_pos_from_pid(int pid, int* workers, int n_workers) { 189 | for (int i = 0; i < n_workers; i++) { 190 | if (pid == workers[i]) 191 | return i; 192 | } 193 | return FAILED; 194 | } 195 | 196 | // find out how many files are in a directory 197 | int n_files_in_dir(char* path) { 198 | int count = 0; 199 | DIR* directory; 200 | struct dirent* entry; 201 | directory = opendir(path); 202 | while ((entry = readdir(directory)) != NULL) { 203 | if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) 204 | count++; 205 | } 206 | closedir(directory); 207 | return count; 208 | } 209 | 210 | // find out how many files a worker has 211 | int n_files_in_worker(char* path, List countries) { 212 | int count = 0; 213 | for (int i = 0; i < countries->size; i++) { 214 | count += n_files_in_dir(concat(path, (char*)list_nth(countries, i))); 215 | } 216 | return count; 217 | } 218 | -------------------------------------------------------------------------------- /DiseaseServer/whoClient/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = client 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build 6 | SRC_DIRS = . ../modules 7 | INC_DIRS = ../include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name "*.c") 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ -lpthread 17 | 18 | # c source 19 | $(BUILD_DIR)/%.c.o: %.c 20 | $(MKDIR_P) $(dir $@) 21 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 22 | 23 | 24 | .PHONY: clean 25 | 26 | clean: 27 | $(RM) -r $(BUILD_DIR) 28 | $(RM) $(TARGET_EXEC) 29 | 30 | -include $(DEPS) 31 | 32 | MKDIR_P ?= mkdir -p -------------------------------------------------------------------------------- /DiseaseServer/whoClient/client_main.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | 3 | // prototype for our operation function 4 | void client_operation(int n_threads, char* query_file, char* server_ip, char* server_port); 5 | 6 | int main(int argc, char* argv[]) { 7 | int num_threads; 8 | char* query_file, *server_port, *server_ip; 9 | // check the arguments given 10 | if (argc != 9) { 11 | fprintf(stderr, "Usage: ./whoClient –q queryFile -w numThreads –sp servPort –sip servIP\n"); 12 | exit(EXIT_FAILURE); 13 | } 14 | if (! strcmp(argv[1], "-q")) { 15 | query_file = strdup(argv[2]); 16 | } else { 17 | fprintf(stderr, "Usage: ./whoClient –q queryFile -w numThreads –sp servPort –sip servIP\n"); 18 | exit(EXIT_FAILURE); 19 | } 20 | if (! strcmp(argv[3], "-w")) { 21 | // TODO: Maybe avoid many threads 22 | num_threads = atoi(argv[4]); 23 | } else { 24 | fprintf(stderr, "Usage: ./whoClient –q queryFile -w numThreads –sp servPort –sip servIP\n"); 25 | exit(EXIT_FAILURE); 26 | } 27 | if (! strcmp(argv[5], "-sp")) { 28 | server_port = strdup(argv[6]); 29 | } else { 30 | fprintf(stderr, "Usage: ./whoClient –q queryFile -w numThreads –sp servPort –sip servIP\n"); 31 | exit(EXIT_FAILURE); 32 | } 33 | if (! strcmp(argv[7], "-sip")) { 34 | server_ip = strdup(argv[8]); 35 | } else { 36 | fprintf(stderr, "Usage: ./whoClient –q queryFile -w numThreads –sp servPort –sip servIP\n"); 37 | exit(EXIT_FAILURE); 38 | } 39 | // call the operate function in order for the client to do his job 40 | client_operation(num_threads, query_file, server_ip, server_port); 41 | // free those in order to not have any leaks 42 | free(query_file); 43 | free(server_ip); 44 | free(server_port); 45 | // everything is sound! 46 | exit(EXIT_SUCCESS); 47 | } -------------------------------------------------------------------------------- /DiseaseServer/whoClient/client_operation.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "common_functions.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // struct that contains the info needed for each thread 10 | struct init_info { 11 | char* server_ip; 12 | char* server_port; 13 | char* query; 14 | }; 15 | 16 | typedef struct init_info* info; 17 | 18 | // global barrier variable that is accessible from all of the threads 19 | pthread_barrier_t barrier; 20 | 21 | // use a macro to initialize our mutex 22 | pthread_mutex_t counter_lock = PTHREAD_MUTEX_INITIALIZER; 23 | 24 | Pointer thread_operate(Pointer q) { 25 | // wait for all threads to initialize 26 | pthread_barrier_wait(&barrier); 27 | // lock the mutex so we are sure that only this thread is connexting to the server 28 | pthread_mutex_lock(&counter_lock); 29 | info wrap = (info)q; 30 | struct sockaddr_in server; 31 | struct sockaddr* server_ptr = (struct sockaddr*)&server; 32 | struct hostent* rem; 33 | int sock; 34 | // create a socket and check for a possible error 35 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 36 | perror("socket"); 37 | exit(EXIT_FAILURE); 38 | } 39 | // find the server 40 | if ((rem = gethostbyname(wrap->server_ip)) == NULL) { 41 | herror("gethostbyname"); 42 | exit(EXIT_FAILURE); 43 | } 44 | // convert the port to an integer for later 45 | int port = atoi(wrap->server_port); 46 | // specify that we are talking about an internet domain 47 | server.sin_family = AF_INET; 48 | memcpy(&server.sin_addr, rem->h_addr, rem->h_length); 49 | server.sin_port = htons(port); 50 | // try to connect 51 | if (connect(sock, server_ptr, sizeof(server)) < 0) { 52 | perror("connect"); 53 | exit(EXIT_FAILURE); 54 | } 55 | fprintf(stdout, "Connection to server initialized by thread %ld\n", pthread_self()); 56 | // inform the server that we are a client 57 | write(sock, "c", sizeof(char)); 58 | // write the instruction to the server 59 | int res = write_to_socket(sock, wrap->query, strlen(wrap->query) + 1); 60 | if (res < 0) { 61 | perror("write"); 62 | exit(EXIT_FAILURE); 63 | } 64 | // read the answer from the server 65 | char* answer = read_from_socket(sock); 66 | // print the answer; 67 | fprintf(stdout, "\n\nQuery: %s\nAnswer:\n%s\n---------------------\n", wrap->query, answer); 68 | free(answer); 69 | //Close the connection 70 | close(sock); 71 | sock = -1; 72 | // operation done! unlock the mutex 73 | pthread_mutex_unlock(&counter_lock); 74 | // the thread can now exit. Return the wrap so it xan be freed 75 | pthread_exit(wrap); 76 | } 77 | 78 | void client_operation(int n_threads, char* query_file, char* server_ip, char* server_port) { 79 | // open the query file 80 | FILE* queries = fopen(query_file, "r"); 81 | int lines = nlines(queries); 82 | // specify how many loops must be done 83 | int n_loops = lines / n_threads; 84 | // and how many threads we will create in the last loop 85 | int remainder = lines % n_threads; 86 | // if there are less lines than threads, then there is no point in creating nthreads 87 | int active_threads = (n_threads >= lines) ? lines : n_threads; 88 | // do n_loops full loops by creating all the threads 89 | for (int i = 0; i < n_loops + 1; i++) { 90 | int curr_threads; 91 | // if we are in the first n-1 loops, we want the maximum looops 92 | if (i < n_loops) { 93 | curr_threads = active_threads; 94 | } 95 | // else, only the remaining ones 96 | else { 97 | // check if there is at least one 98 | if (remainder) 99 | curr_threads = remainder; 100 | // else just breadk the loop 101 | else 102 | break; 103 | } 104 | // allocate space to store the thread ids 105 | pthread_t* thread_ids = malloc(curr_threads * sizeof(*thread_ids)); 106 | if (thread_ids == NULL) { 107 | perror("malloc"); 108 | exit(EXIT_FAILURE); 109 | } 110 | pthread_barrier_init(&barrier, NULL, curr_threads); 111 | char* query = NULL; 112 | size_t len = 0; 113 | for (int j = 0; j < curr_threads; j++) { 114 | info wrap = malloc(sizeof(*wrap)); 115 | // read the query from the file given 116 | int read = getline(&query, &len, queries); 117 | // catch a possible error 118 | if (!read) { 119 | perror("getline:"); 120 | exit(EXIT_FAILURE); 121 | } 122 | // initialize the info thar we're gonna send to the threads 123 | wrap->query = strdup(query); 124 | wrap->server_ip = server_ip; 125 | wrap->server_port = server_port; 126 | // create a new thread 127 | int res = pthread_create(&thread_ids[j], NULL, thread_operate, wrap); 128 | // catch a possible error 129 | if (res) { 130 | perror2("pthread_create", res); 131 | exit(EXIT_FAILURE); 132 | } 133 | } 134 | free(query); 135 | 136 | // wait for each thread to finish its execution 137 | for (int j = 0; j < curr_threads; j++) { 138 | Pointer w; 139 | // join all the created threads 140 | int res = pthread_join(thread_ids[j], &w); 141 | if (res) { 142 | perror2("pthread_join", res); 143 | exit(EXIT_FAILURE); 144 | } 145 | // free the returned wrap 146 | info wrap = (info)w; 147 | free(wrap->query); 148 | free(wrap); 149 | } 150 | pthread_barrier_destroy(&barrier); 151 | free(thread_ids); 152 | } 153 | // close the queries file, we do not need it anymore 154 | fclose(queries); 155 | // everything is perfect! return to wrap it up! 156 | } 157 | -------------------------------------------------------------------------------- /DiseaseServer/whoServer/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = server 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build 6 | SRC_DIRS = . ../modules 7 | INC_DIRS = ../include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name "*.c") 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ -lpthread 17 | 18 | # c source 19 | $(BUILD_DIR)/%.c.o: %.c 20 | $(MKDIR_P) $(dir $@) 21 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 22 | 23 | 24 | .PHONY: clean 25 | 26 | clean: 27 | $(RM) -r $(BUILD_DIR) 28 | $(RM) $(TARGET_EXEC) 29 | 30 | -include $(DEPS) 31 | 32 | MKDIR_P ?= mkdir -p -------------------------------------------------------------------------------- /DiseaseServer/whoServer/buffer.c: -------------------------------------------------------------------------------- 1 | #include "buffer.h" 2 | #include 3 | 4 | extern pthread_mutex_t mtx; 5 | extern pthread_cond_t cond_nonempty; 6 | extern pthread_cond_t cond_nonfull; 7 | 8 | // initialize a new buffer 9 | Buffer initialize_buffer(int size) { 10 | Buffer new_buff = malloc(sizeof(*new_buff)); 11 | new_buff->data = malloc(size * sizeof(int)); 12 | new_buff->start = 0; 13 | new_buff->end = -1; 14 | new_buff->count = 0; 15 | new_buff->size = size; 16 | return new_buff; 17 | } 18 | 19 | // place an item in a buffer 20 | void place(Buffer buffer, int data) { 21 | // shared data area -> lock the mutex 22 | pthread_mutex_lock(&mtx); 23 | // wait for a signal if the buffer is full 24 | while (buffer->count >= buffer->size) { 25 | pthread_cond_wait(&cond_nonfull, &mtx); 26 | } 27 | // update the end pointer in te buffer 28 | buffer->end = (buffer->end + 1) % buffer->size; 29 | // insert the data in the correct position 30 | buffer->data[buffer->end] = data; 31 | // a new item is in, increase our count 32 | buffer->count++; 33 | // shared memory area done, unlock the mutex 34 | pthread_mutex_unlock(&mtx); 35 | } 36 | 37 | int obtain (Buffer buffer) { 38 | // shared data area -> lock the mutex 39 | pthread_mutex_lock(&mtx); 40 | // wait for a signal if the buffer is empty 41 | while (buffer->count <= 0) { 42 | pthread_cond_wait(&cond_nonempty, &mtx); 43 | } 44 | // obtain the data from the 1st position of the buffer 45 | int data = buffer->data[buffer->start]; 46 | // update the position that we're gonna read next time 47 | buffer->start = (buffer->start + 1) % buffer->size; 48 | // we've removed an item form the buffer 49 | buffer->count--; 50 | // shared memory area done, unlock the mutex 51 | pthread_mutex_unlock(&mtx); 52 | // return the data we've just read 53 | return data; 54 | } 55 | 56 | // destroy an existing buffer 57 | void destroy_buffer(Buffer buff) { 58 | assert(buff != NULL); 59 | free(buff->data); 60 | free(buff); 61 | } -------------------------------------------------------------------------------- /DiseaseServer/whoServer/server_main.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | 3 | // prototype for our operation function 4 | void server_operation(char* query_port, char* stats_port, int buffer_size, int num_threads); 5 | 6 | int main(int argc, char* argv[]) { 7 | int num_threads, buffer_size; 8 | char* stats_port, *query_port; 9 | // check the arguments given 10 | if (argc != 9) { 11 | fprintf(stderr, "Usage: ./whoServer –q queryPortNum -s statisticsPortNum –w numThreads –b bufferSize\n"); 12 | exit(EXIT_FAILURE); 13 | } 14 | if (! strcmp(argv[1], "-q")) { 15 | query_port = strdup(argv[2]); 16 | } else { 17 | fprintf(stderr, "Usage: ./whoServer –q queryPortNum -s statisticsPortNum –w numThreads –b bufferSize\n"); 18 | exit(EXIT_FAILURE); 19 | } 20 | if (! strcmp(argv[3], "-s")) { 21 | stats_port = strdup(argv[4]); 22 | } else { 23 | fprintf(stderr, "Usage: ./whoServer –q queryPortNum -s statisticsPortNum –w numThreads –b bufferSize\n"); 24 | exit(EXIT_FAILURE); 25 | } 26 | if (! strcmp(argv[5], "-w")) { 27 | num_threads = atoi(argv[6]); 28 | } else { 29 | fprintf(stderr, "Usage: ./whoServer –q queryPortNum -s statisticsPortNum –w numThreads –b bufferSize\n"); 30 | exit(EXIT_FAILURE); 31 | } 32 | if (! strcmp(argv[7], "-b")) { 33 | buffer_size = atoi(argv[8]); 34 | } else { 35 | fprintf(stderr, "Usage: ./whoServer –q queryPortNum -s statisticsPortNum –w numThreads –b bufferSize\n"); 36 | exit(EXIT_FAILURE); 37 | } 38 | // call the operate function in order for the client to do his job 39 | server_operation(query_port, stats_port, buffer_size, num_threads); 40 | // free those in order to not have any leaks 41 | free(stats_port); 42 | free(query_port); 43 | // everything is sound! 44 | exit(EXIT_SUCCESS); 45 | } -------------------------------------------------------------------------------- /DiseaseServer/worker/Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC = worker 2 | 3 | CC = gcc 4 | 5 | BUILD_DIR = ./build 6 | SRC_DIRS = . ../modules 7 | INC_DIRS = ../include 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name "*.c") 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | CC_FLAGS = -Wall -g -I$(INC_DIRS) 14 | 15 | $(TARGET_EXEC): $(OBJS) 16 | $(CC) $(OBJS) -o $@ -lpthread 17 | 18 | # c source 19 | $(BUILD_DIR)/%.c.o: %.c 20 | $(MKDIR_P) $(dir $@) 21 | $(CC) -c $(CC_FLAGS) $< -o $@ -lm 22 | 23 | 24 | .PHONY: clean 25 | 26 | clean: 27 | $(RM) -r $(BUILD_DIR) 28 | $(RM) $(TARGET_EXEC) 29 | 30 | -include $(DEPS) 31 | 32 | MKDIR_P ?= mkdir -p -------------------------------------------------------------------------------- /DiseaseServer/worker/worker_main.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "common_functions.h" 3 | #include "HashTable.h" 4 | #include "BalancedTree.h" 5 | #include "Patient.h" 6 | #include "LinkedList.h" 7 | #include "WorkerMenu.h" 8 | #include 9 | #include "Queries.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | // definition of parsing function 22 | void parser(char* input_dir, List dirs, int writing, HashTable patients, HashTable diseases_hash); 23 | 24 | // global variable to catch a signal 25 | volatile sig_atomic_t sig_int_raised; 26 | 27 | // catch an insterupt 28 | void catch_int(int signo) { 29 | sig_int_raised = signo; 30 | } 31 | 32 | // function to send a signal to parent if the connection to the server fails 33 | void perror_and_sig(char* err) { 34 | // print the error 35 | perror(err); 36 | // send a signal to the master 37 | kill(getppid(), SIGUSR1); 38 | // wait to be killed 39 | while (true); 40 | } 41 | 42 | int main(int argc, char* argv[]) { 43 | // iniialize a signal set 44 | static struct sigaction act_int; 45 | // handle our signals with our function, in order to catch them 46 | act_int.sa_handler = catch_int; 47 | sigfillset(&(act_int.sa_mask)); 48 | // want to handle SIGINT and SIGQUIT with this function 49 | sigaction(SIGINT, &act_int, NULL); 50 | sigaction(SIGQUIT, &act_int, NULL); 51 | 52 | // Get the pipe names from the args, and open them: 53 | // 1st for writing, 2nd for reading 54 | int reading; 55 | int buff_size = atoi(argv[2]); 56 | char* input_dir = concat(argv[3], "/"); 57 | reading = open(argv[1], O_RDONLY, 0666); 58 | // check for possible errors while opening the pipes 59 | if (reading == -1) { 60 | perror_and_sig("open"); 61 | } 62 | // Create a hash table to store all the different diseases 63 | HashTable diseases_hash = hash_create(HASH_SIZE, hash_strings, BUCKET_SIZE, balanced_tree_destroy); 64 | // We are going to use one extra hashtable, in order to quickly search if the patient allready exists 65 | // We are going to pass destroy patient as a destroy func, in order to free all the memory occupied 66 | // by the patients when we are done. 67 | HashTable patients = hash_create(HASH_SIZE, hash_strings, sizeof(Patient) + sizeof(Pointer), destroy_patient); 68 | // create a list to store all the directories that the worker must handle 69 | List dirs = create_list(compare_strings, free); //TODO: Maybe NULL for destroy 70 | char* str; 71 | // read the dirs from the pipe 72 | while (true) { 73 | str = read_from_pipe(reading, buff_size); 74 | // break when "end" is sent by the parent 75 | if (! strcmp(str, "end")) 76 | break; 77 | list_insert(dirs, str); 78 | } 79 | // Now, we must open a new conection as a server, in order to answer to queries 80 | struct sockaddr_in q_server, q_client; 81 | struct sockaddr* q_serverptr = (struct sockaddr*)&q_server; 82 | struct sockaddr* q_clientptr = (struct sockaddr*)&q_client; 83 | int q_sock; 84 | // create the socket for the queries 85 | if ((q_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 86 | perror_and_sig("socket"); 87 | exit(EXIT_FAILURE); 88 | } 89 | // initialize our internet domain 90 | q_server.sin_family = AF_INET; 91 | q_server.sin_addr.s_addr = htonl(INADDR_ANY); 92 | // initialize with port 0, so we let the os determine which port it is gonna give us 93 | q_server.sin_port = htons(0); 94 | // bind the socket to the address 95 | if (bind(q_sock, q_serverptr, sizeof(q_server)) < 0) { 96 | perror_and_sig("bind"); 97 | } 98 | // listen for incoming connections 99 | if (listen(q_sock, SOMAXCONN) < 0) { 100 | perror_and_sig("listen"); 101 | } 102 | // learn the port that was assigned by the os 103 | struct sockaddr* temp = q_serverptr; 104 | socklen_t len = sizeof(temp); 105 | getsockname(q_sock, temp, &len); 106 | struct sockaddr_in* temp_in = (struct sockaddr_in*)temp; 107 | int queries_port = ntohs(temp_in->sin_port); 108 | // also read the server's ip and port from the pipe 109 | char* server_ip = read_from_pipe(reading, buff_size); 110 | char* server_port = read_from_pipe(reading, buff_size); 111 | // proceed only if the worker is assigned with at least one country 112 | if (!is_empty(dirs)) { 113 | // initiate a connection with the server in order to send : 114 | // 1) a port that we will listen to for queries 115 | // 2) the countries that we are responsible for 116 | // 3) the stats from the files that we parsed 117 | 118 | // initiate the connection to the server 119 | struct sockaddr_in server; 120 | struct sockaddr* server_ptr = (struct sockaddr*)&server; 121 | struct hostent* rem; 122 | int sock; 123 | // create a socket and check for a possible error 124 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 125 | perror_and_sig("socket"); 126 | } 127 | // find the server 128 | if ((rem = gethostbyname(server_ip)) == NULL) { 129 | perror_and_sig("gethostbyname"); 130 | } 131 | // specify that we are talking about an internet domain 132 | server.sin_family = AF_INET; 133 | memcpy(&server.sin_addr, rem->h_addr, rem->h_length); 134 | int s_port = atoi(server_port); 135 | server.sin_port = htons(s_port); 136 | // try to connect 137 | if (connect(sock, server_ptr, sizeof(server)) < 0) { 138 | perror_and_sig("connect"); 139 | } 140 | fprintf(stderr, "Connection to server initialized by worker %d\n", getpid()); 141 | // inform the server that we are going to send worker info 142 | write(sock, "w", sizeof(char)); 143 | // write the query port to the server 144 | int res = write(sock,&queries_port, sizeof(int)); 145 | if (res < 0) { 146 | perror_and_sig("write"); 147 | } 148 | // write how many dirs we are going to send 149 | write(sock, &(dirs->size), sizeof(int)); 150 | // write the dirs to inform the server 151 | for (int i = 0; i < dirs->size; i++) { 152 | char* nth = list_nth(dirs, i); 153 | write_to_socket(sock, nth, strlen(nth)); 154 | } 155 | // call the function to parse the whole input and send the stats to the server 156 | parser(input_dir, dirs, sock, patients, diseases_hash); 157 | // we are ready to answer to queries! 158 | fprintf(stderr, "\n"); 159 | fprintf(stderr, "Worker %d ready to answer queries!\n", getpid()); 160 | // read queries until we break 161 | while (true) { 162 | // accept a new connection 163 | int new_sock; 164 | socklen_t client_len; 165 | if ((new_sock = accept(q_sock, q_clientptr, &client_len)) < 0) { 166 | perror_and_sig("accept"); 167 | } 168 | // read the query form the socket 169 | char* query = read_from_socket(new_sock); 170 | // check for a possible signal 171 | // If a sigint or sigquit are caught 172 | if (sig_int_raised) { 173 | // free out data structures 174 | hash_destroy(diseases_hash); 175 | destroy_list(dirs); 176 | // close the fd 177 | close(reading); 178 | // exit 179 | exit(EXIT_SUCCESS); 180 | } 181 | if (query != NULL) { 182 | // call the menu to analyze the query and write the result to the socket 183 | worker_menu(query, dirs, patients, diseases_hash, new_sock); 184 | // close the socket 185 | close(new_sock); 186 | } 187 | } 188 | } else { 189 | // if no countries are assigned, just wait until the parent sends a SIGKILL 190 | while(true); 191 | // check for a possible signal 192 | // If a sigint or sigquit are caught 193 | if (sig_int_raised) { 194 | // free out data structures 195 | hash_destroy(diseases_hash); 196 | destroy_list(dirs); 197 | // close the fd 198 | close(reading); 199 | // exit 200 | exit(EXIT_SUCCESS); 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /DiseaseServer/worker/worker_menu.c: -------------------------------------------------------------------------------- 1 | #include "common_types.h" 2 | #include "WorkerMenu.h" 3 | #include "common_functions.h" 4 | #include "Queries.h" 5 | bool worker_menu(char* qu, List dirs, HashTable patients, HashTable diseases_hash, int writing) { 6 | char* query = strdup(qu); 7 | 8 | char delim[3] = " \n"; 9 | if (strstr(query, "/diseaseFrequency")) { 10 | if (n_words(query) < 4 || n_words(query) > 5) { 11 | fprintf(stderr, "error\n"); 12 | write_to_socket(writing, "-", strlen("-")); 13 | return false; 14 | } 15 | char* q = strtok(query, delim); 16 | if (strcmp(q, "/diseaseFrequency")) { 17 | free(qu); 18 | return false; 19 | } 20 | char* virus = strtok(NULL, delim); 21 | char* arg2 = strtok(NULL, delim); 22 | char* arg3 = strtok(NULL, delim); 23 | char* country = strtok(NULL, delim); 24 | int res = 0; 25 | // if a country is specified, then just return the number 26 | if (country) { 27 | res = disease_frequency(virus, arg2, arg3, country, diseases_hash); 28 | } 29 | else { 30 | for (int i = 0; i < dirs->size; i++) { 31 | char* curr_country = list_nth(dirs,i); 32 | res += disease_frequency(virus, arg2, arg3, curr_country, diseases_hash); 33 | } 34 | free(qu); 35 | } 36 | char* result = itoa(res); 37 | write_to_socket(writing, result, strlen(result)); 38 | free(result); 39 | return true; 40 | } 41 | else if (strstr(query, "/topk-AgeRanges")) { 42 | if (n_words(query) != 6) { 43 | fprintf(stderr, "error\n"); 44 | free(qu); 45 | return false; 46 | } 47 | char* q = strtok(query, delim); 48 | if (strcmp(q, "/topk-AgeRanges")) { 49 | free(qu); 50 | return false; 51 | } 52 | // Analyze the input 53 | int k = atoi(strtok(NULL, delim)); 54 | char* country = strtok(NULL, delim); 55 | char* disease = strtok(NULL, delim); 56 | char* day1 = strtok(NULL, delim); 57 | char* day2 = strtok(NULL, delim); 58 | topk_age_ranges(k, country, disease, day1, day2, diseases_hash, writing); 59 | return true; 60 | } 61 | else if (strstr(query, "/searchPatientRecord")) { 62 | if (n_words(query) != 2) { 63 | fprintf(stderr, "error\n"); 64 | free(qu); 65 | return false; 66 | } 67 | char* q = strtok(query, delim); 68 | if (strcmp(q, "/searchPatientRecord")) { 69 | free(qu); 70 | return false; 71 | } 72 | char* r_id = strtok(NULL, delim); 73 | char* result = search_patient_record(r_id, patients); 74 | write_to_socket(writing, result, strlen(result)); 75 | if (!strcmp(result, "-")) 76 | return false; 77 | else 78 | return true; 79 | } 80 | else if (strstr(query, "/numPatientAdmissions")) { 81 | if (n_words(query) < 4 || n_words(query) > 5) { 82 | fprintf(stderr, "error\n"); 83 | free(qu); 84 | return false; 85 | } 86 | char* q = strtok(query, delim); 87 | if (strcmp(q, "/numPatientAdmissions")) { 88 | free(qu); 89 | return false; 90 | } 91 | char* virus = strtok(NULL, delim); 92 | char* arg2 = strtok(NULL, delim); 93 | char* arg3 = strtok(NULL, delim); 94 | char* country = strtok(NULL, delim); 95 | // If the query is specified for 1 country 96 | if (country) { 97 | // just write it to the pipe 98 | int sum = num_patient_admissions(virus, arg2, arg3, country, diseases_hash); 99 | char* result = itoa(sum); 100 | write_to_socket(writing, result, strlen(result)); 101 | free(result); 102 | return true; 103 | } 104 | else { 105 | int result = 0; 106 | // for each one of the countries 107 | for (int i = 0; i < dirs->size; i++) { 108 | // call the query function with a specific country 109 | char* curr_country = list_nth(dirs, i); 110 | // increase the sum by the amount of patients in each call 111 | result += num_patient_admissions(virus, arg2, arg3, curr_country, diseases_hash); 112 | } 113 | char* final = itoa(result); 114 | write_to_socket(writing, final, strlen(final)); 115 | free(final); 116 | free(qu); 117 | return true; 118 | } 119 | } 120 | else if (strstr(query, "/numPatientDischarges")) { 121 | if (n_words(query) < 4 || n_words(query) > 5) { 122 | fprintf(stderr, "error\n"); 123 | free(qu); 124 | return false; 125 | } 126 | char* q = strtok(query, delim); 127 | if (strcmp(q, "/numPatientDischarges")) { 128 | free(qu); 129 | return false; 130 | } 131 | char* virus = strtok(NULL, delim); 132 | char* arg2 = strtok(NULL, delim); 133 | char* arg3 = strtok(NULL, delim); 134 | char* country = strtok(NULL, delim); 135 | // If the query is specified for 1 country 136 | if (country) { 137 | // just write it to the pipe 138 | int sum = num_patient_admissions(virus, arg2, arg3, country, diseases_hash); 139 | char* result = itoa(sum); 140 | write_to_socket(writing, result, strlen(result)); 141 | free(result); 142 | return true; 143 | } 144 | else { 145 | int result = 0; 146 | // for each one of the countries 147 | for (int i = 0; i < dirs->size; i++) { 148 | // call the query function with a specific country 149 | char* curr_country = list_nth(dirs, i); 150 | // increase the sum by the amount of patients in each call 151 | result += num_patient_admissions(virus, arg2, arg3, curr_country, diseases_hash); 152 | } 153 | char* final = itoa(result); 154 | write_to_socket(writing, final, strlen(final)); 155 | free(final); 156 | free(qu); 157 | return true; 158 | } 159 | } else { 160 | fprintf(stderr, "Query not recognized by worker: %s\n", qu); 161 | free(qu); 162 | return false; 163 | } 164 | } -------------------------------------------------------------------------------- /DiseaseServer/worker/worker_queries.c: -------------------------------------------------------------------------------- 1 | #include "Queries.h" 2 | 3 | //=================================== Hepful functions needed for the queries ===============================// 4 | 5 | // Compare function for strings 6 | int compare_ids (Pointer a, Pointer b) { 7 | return strcmp((char*) a, (char*)b); 8 | } 9 | // condition function for a tree 10 | bool check_same_country(Pointer ent, Pointer count, Pointer dummy1, Pointer dummy2) { 11 | char* country = (char*) count; 12 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 13 | if (entry!= NULL) { 14 | Patient* patient = entry->assigned_patient; 15 | if (!strcmp(patient->country, country)) 16 | return true; 17 | else 18 | return false; 19 | } else { 20 | printf("Disease not found\n"); 21 | return false; 22 | } 23 | } 24 | 25 | bool check_bigger_entry_date(Pointer ent, Pointer d, Pointer country, Pointer dummy2) { 26 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 27 | Patient* patient = entry->assigned_patient; 28 | Date* date = (Date*)d; 29 | return (compare_dates(patient->entry_date, *date) > 0 && !strcmp(country, patient->country)); 30 | 31 | } 32 | 33 | bool check_bigger_exit_date(Pointer ent, Pointer d, Pointer country, Pointer dummy2) { 34 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 35 | Patient* patient = entry->assigned_patient; 36 | Date* date = (Date*)d; 37 | return (compare_dates(patient->exit_date, *date) > 0) && !strcmp(country, patient->country); 38 | 39 | } 40 | 41 | bool check_age_group(Pointer ent, Pointer a, Pointer d1, Pointer d2) { 42 | BalancedTreeEntry entry = (BalancedTreeEntry)ent; 43 | Patient* patient = entry->assigned_patient; 44 | Date* date1 = (Date*)d1; 45 | Date* date2 = (Date*)d2; 46 | int age = atoi((char*)a); 47 | return (patient->age < age && compare_dates(patient->entry_date, *date1) > 0 && compare_dates(*date2, patient->entry_date)); 48 | } 49 | //========================================= Queries for the monitor =========================================// 50 | 51 | // Given a patient record, we want to mark his exit date. On success we return true, otherwise false 52 | bool recordPatientExit(char* info, HashTable patients, char* exit_str) { 53 | char* exit_d = strdup(exit_str); 54 | char delim[3] = " \n"; 55 | char* r_id = strtok(info, delim); 56 | if (r_id == NULL || exit_d == NULL) { 57 | return false; 58 | } 59 | // Mark the exit date 60 | Date exit_date = string_to_date(exit_d); 61 | free(exit_d); 62 | // Search for the patient 63 | HashEntry patient_entry = hash_search(patients, r_id); 64 | if (patient_entry == NULL) { 65 | return false; 66 | } 67 | // The patient record can be easily found in the patients ht, where a pointer to the record is kept 68 | Patient* patient = (Patient*)patient_entry->item; 69 | // Check of the patient has already exited the hospital 70 | if (check_valid_dates(patient->entry_date, exit_date)) { 71 | // Update the desired info 72 | patient->exit_date = exit_date; 73 | return true; 74 | } else { 75 | return false; 76 | } 77 | } 78 | 79 | // This query is called by each worker for each country, thus the country argument will always be present 80 | int disease_frequency(char* virus, char* arg2, char* arg3, char* country, HashTable diseases_hash) { 81 | char* a2 = strdup(arg2); 82 | char* a3 = strdup(arg3); 83 | 84 | Date d1 = string_to_date(a2); 85 | Date d2 = string_to_date(a3); 86 | free(a2); free(a3); 87 | if (!check_valid_dates(d1, d2)) { 88 | fprintf(stderr, "Wrong dates\n"); 89 | return FAILED; 90 | } 91 | HashEntry entry = hash_search(diseases_hash, virus); 92 | if (entry == NULL) { 93 | printf("Desired disease not found\n"); 94 | return FAILED; 95 | } else { 96 | // Insert the same country as a condition to the previous function. 97 | BalancedTree tree = entry->item; 98 | int g_than = total_nodes_grater_than(tree, &d1, check_same_country, country) - total_nodes_grater_than(tree, &d2, check_same_country, country); 99 | return g_than; 100 | } 101 | } 102 | 103 | char* search_patient_record(char* r_id, HashTable patients) { 104 | // search for the wanted record in the patients hash 105 | HashEntry result = hash_search(patients, r_id); 106 | // if we do not find it just return NULL 107 | if (result == NULL) 108 | return "-"; 109 | // else, construct the appropriate string to store the patient record 110 | Patient* p = result->item; 111 | char* entry_date = date_to_string(p->entry_date); 112 | char* exit_date = date_to_string(p->exit_date); 113 | char* patient = malloc((sizeof(*p) + 10) * sizeof(*patient)); 114 | snprintf(patient, (sizeof(*p) + 10), "%s %s %s %s %d %s %s", r_id, p->first_name, p->last_name, p->disease, p->age, entry_date, exit_date); 115 | // return that string 116 | return patient; 117 | } 118 | 119 | int num_patient_admissions(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash) { 120 | // Convert the input strings to dates 121 | char* a2 = strdup(arg2); 122 | char* a3 = strdup(arg3); 123 | Date d1 = string_to_date(a2); 124 | Date d2 = string_to_date(a3); 125 | free(a2); free(a3); 126 | BalancedTree disease_tree = hash_search(diseases_hash, disease)->item; 127 | // If there is no such entry in the disease ht, then we do not have any patients here 128 | if (disease_tree == NULL) { 129 | return FAILED; 130 | } 131 | // all the ones that are after date 2, except those that are after date 1 132 | return balanced_tree_cond_traverse(disease_tree, check_bigger_entry_date, &d1, country, NULL) - balanced_tree_cond_traverse(disease_tree, check_bigger_entry_date, &d2, country, NULL); 133 | } 134 | 135 | int num_patient_discharges(char* disease, char* arg2, char* arg3, char* country, HashTable diseases_hash) { 136 | // Convert the input strings to dates 137 | char* a2 = strdup(arg2); 138 | char* a3 = strdup(arg3); 139 | Date d1 = string_to_date(a2); 140 | Date d2 = string_to_date(a3); 141 | free(a2); free(a3); 142 | BalancedTree disease_tree = hash_search(diseases_hash, disease)->item; 143 | // If there is no such entry in the disease ht, then we do not have any patients here 144 | if (disease_tree == NULL) { 145 | return FAILED; 146 | } 147 | // all the ones that are after date 2, except those that are after date 1 148 | return balanced_tree_cond_traverse(disease_tree, check_bigger_exit_date, &d1, country, NULL) - balanced_tree_cond_traverse(disease_tree, check_bigger_exit_date, &d2, country, NULL); 149 | } 150 | 151 | void topk_age_ranges(int k, char* country, char* disease, char* day1, char* day2, HashTable diseases_hash, int writing) { 152 | // Convert the input strings to dates 153 | Date d1 = string_to_date(day1); 154 | Date d2 = string_to_date(day2); 155 | BalancedTree disease_tree = hash_search(diseases_hash, disease)->item; 156 | int age_gr1 = balanced_tree_cond_traverse(disease_tree, check_age_group, "20", &d1, &d2); 157 | int age_gr2 = balanced_tree_cond_traverse(disease_tree, check_age_group, "40", &d1, &d2); 158 | int age_gr3 = balanced_tree_cond_traverse(disease_tree, check_age_group, "60", &d1, &d2); 159 | int age_gr4 = balanced_tree_cond_traverse(disease_tree, check_age_group, "120", &d1, &d2); 160 | int total = age_gr1 + age_gr2 + age_gr3 + age_gr4; 161 | Heap heap = create_heap(NULL); 162 | heap_insert(heap, age_gr1, "0-20"); 163 | heap_insert(heap, age_gr2, "20-40"); 164 | heap_insert(heap, age_gr3, "40-60"); 165 | heap_insert(heap, age_gr4, "60+"); 166 | // define how many times we will write in the pipes, and send this number so the parent is aware too 167 | 168 | int k_ret = (k < 4) ? k : 4; 169 | char* result = itoa(k_ret); 170 | write_to_socket(writing, result, strlen(result)); 171 | char* ret = malloc(13 * sizeof(*ret)); 172 | for (int i = 0; i key, 100 * ent->priority / total); 175 | write_to_socket(writing, ret, strlen(ret)); 176 | free(ent); 177 | } 178 | free(ret); 179 | destroy_heap(heap); 180 | } 181 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nikos Galanis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SysPro Assignments 2 | 3 | This set of assignments aims to construct C programs that will take as input virus data, they will process and record them, and will finally answer queries about those cases. 4 | 5 | ## DiseaseMonitor 6 | 7 | DiseaseMonitor is centered around the data structures created and used, such as an AVL tree, and a complicated Hash Table. The programs takes as input various cases from different countries, and answers to queries about those records. 8 | 9 | 10 | ### Compiling & running 11 | 12 | - Compile the project simply by typing `make` 13 | - Run like this: `build/diseaseMonitor -p patientRecordsFile –h1 diseaseHashtableNumOfEntries –h2 countryHashtableNumOfEntries –b bucketSize` where bucketsize stands for the hash table node size in bytes. 14 | 15 | ## DiseaseAggregator 16 | 17 | DiseaseAggregator is centered around inter-process communication, using mostly pipes and signals. A bash script generating sample data is also provided. The queries are the same with Monitor. 18 | 19 | ### Compiling & running 20 | 21 | - Compile the project simply by typing `make` 22 | - Run like this: `./diseaseAggregator –w numWorkers -b bufferSize -i input_dir` where buffersize stands for the size of the buffer used to exchange data through the pipes. 23 | 24 | ## DiseaseServer 25 | 26 | DiseaseServer is centered around network commutication, following the client-server model through TCP, using threads. The application-layer protocol makes use of verification messages to preserve data integrity across processes. The dataa structures used, as well as the queries are similar to the two previous projects. 27 | 28 | ### Compiling & running 29 | 30 | - Compile the project simply by typing `make` 31 | - Run like this: `./master –w numWorkers -b bufferSize –s serverIP –p serverPort -i input_dir` where buffersize stands for the size of the buffer used to exchange data through the pipes. 32 | 33 | ------------------------------------ 34 | 35 | ## Available Queries 36 | 37 | The queries available for every project are the following 38 | 39 | #### /globalDiseaseStats [date1 date2] 40 | Returns for every virus recorded, the number of patients recorded in the system, optionally between 2 dates 41 | 42 | #### /diseaseFrequency virusName date1 date2 [country] 43 | Returns for every virus recorded, the number of patients recorded between 2 dates, optionally, in a specified countries. 44 | 45 | #### /topk-Diseases k country [date1 date2] 46 | Returns the top k diseases that are recorded in the country sepcified, allong with the number of cases, optionally between the 2 dates given. 47 | 48 | #### /topk-Countries k disease [date1 date2] 49 | Returns the top k countries that the specified disease is found, allong with the number of cases, optionally between the 2 dates given. 50 | 51 | #### /insertPatientRecord recordID patientFirstName patientLastName diseaseID country entryDate [exitDate] 52 | Inserts a new patient record in the systems 53 | 54 | #### /recordPatientExit recordID exitDate 55 | Adds an exit date to the specified patient 56 | 57 | #### /numCurrentPatients [disease] 58 | Prints the currently hospitalized patients, optionally with a specified disease 59 | 60 | ## Input type 61 | 62 | An patient record could do like this 63 | ` 889 Mary Smith COVID-2019 China 25-1-2020 27-1-2020` 64 | 65 | ## Input files 66 | 67 | In every project, in the directory `input`, several input files can be found 68 | 69 | ## Getting Started 70 | 71 | Make sure you have a gcc compiler 72 | 73 | ## Downloading 74 | 75 | Download source code by typing: 76 | 77 | ```git clone https://github.com/nikosgalanis/SysPro-Assignments.git``` --------------------------------------------------------------------------------