-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGraph.c
503 lines (470 loc) · 16.3 KB
/
Graph.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
#include "Graph.h"
/**
* Initialize all arraylists.
*/
void Graph_init(Graph *graph) {
graph->degrees = malloc(sizeof(ArrayList));
graph->departments = malloc(sizeof(ArrayList));
graph->students = malloc(sizeof(ArrayList));
ArrayList_init(graph->degrees);
ArrayList_init(graph->departments);
ArrayList_init(graph->students);
}
void Graph_addDepartment(Graph *graph, Department *department) {
ArrayList_push(graph->departments, department);
}
void Graph_addDegree(Graph *graph, Degree *degree) {
ArrayList_push(graph->degrees, degree);
}
void Graph_addStudent(Graph *graph, Student *student) {
ArrayList_push(graph->students, student);
}
/**
* The line contains no \n
*
* First get the department name, check for existence. Then course name and title.
* Finally, use Course_parsePrereqsLine() to parse the prereqs.
*/
void Graph_addCourse(Graph *graph, char *line) {
if (line == NULL) return;
// Store a copy cause strtok changes the line
char courseName[COURSE_NAME_LEN], lineCopy[MAX_LINE_LENGTH];
strcpy(lineCopy, line);
Department *department;
Course *course;
char *str = strtok(lineCopy, ",");
/**
* Runs 4 times only, cause 4 substrings are used
*/
for (int count = 0; count < 4; count++) {
if (str == NULL) {
printf("ERROR ADDING COURSE\n");
return;
}
if (count == 0) { // First, parse department
department = ArrayList_find(graph->departments, str, Department_compareString);
if (department == NULL) {
printf("CANNOT ADD COURSE. DEPARTMENT NOT FOUND\n");
return;
}
} else if (count == 1) { // course name
str++;
strcpy(courseName, str);
} else if (count == 2) { // course title
str++;
course = malloc(sizeof(Course));
Course_init(course, courseName, str);
Department_addCourse(department, course);
} else {
/**
* Now, the rest are the prereqs. To find where it starts, strtok cannot be used.
* We need to count exactly how many chars the past 3 substrings took
* and go forward in line by that amount.
*/
line += strlen(department->name) + strlen(courseName) +
strlen(course->title) + 6;
Course_parsePrereqsLine(course, line);
break;
}
str = strtok(NULL, ",");
}
}
/**
* Look through every department for the course and return the department.
* O(d log(n)). d is usually small so just O(log(n))
*/
Department *Graph_findDepartmentOfCourse(Graph *graph, char *courseName) {
for (size_t i = 0; i < graph->departments->size; i++) {
Department *department = ArrayList_get(graph->departments, i);
Course *course = BinaryTree_find(department->courses, courseName);
if (course != NULL) {
return department;
}
}
return NULL;
}
/**
* Look through every department.
* O(d log n) but d is usually small so O(log(n))
*/
Course *Graph_findCourse(Graph *graph, char *courseName) {
for (size_t i = 0; i < graph->departments->size; i++) {
Department *department = ArrayList_get(graph->departments, i);
Course *course = BinaryTree_find(department->courses, courseName);
if (course != NULL) {
return course;
}
}
return NULL;
}
/**
* Go through every degree and try to find the course in the 2d linked list.
* If found, add the degree to the degrees string separated by the delimiter.
* O(n) because n courses total, each degree has a 2D linked list so it totals up to all courses.
*/
void Graph_findDegreesStr(Graph *graph, char *courseName, char *degrees, char* delimiter) {
Course *course = NULL;
strcpy(degrees, "");
Degree *degree;
bool first = true;
/**
* Go through every degree and check if course in any list.
*/
for (size_t i = 0; i < graph->degrees->size; i++) {
degree = ArrayList_get(graph->degrees, i);
/**
* Use special comparator to make life easy.
*/
course = LinkedList_find(degree->reqs->ands, courseName,
Requirements_innerListFindComparator);
if (course != NULL) {
if (!first) {
strcat(degrees, delimiter);
}
strcat(degrees, degree->name);
first = false;
}
}
}
/**
* First find student and degree then
* [erform set difference for the student's degree and print it
*/
void Graph_describeDegreeReq(Graph *graph, char *studentName) {
Student *student = ArrayList_find(graph->students, studentName, Student_compareString);
if (student == NULL) {
printf("NOT FOUND\n");
return;
}
Degree *degree = ArrayList_find(graph->degrees, student->degree, Degree_compareString);
if (degree == NULL) {
printf("ERROR, DEGREE NOT FOUND.\n");
return;
}
/**
* Create a new Requirements object, to easily print out.
*/
Requirements *remaining = malloc(sizeof(Requirements));
Requirements_init(remaining);
Requirements_findDifference(degree->reqs, student->courses, remaining);
char line[MAX_LINE_LENGTH], delim[] = "\n";
Requirements_toString(remaining, line, delim);
printf("%s", line);
Requirements_free(remaining, dont_free);
free(remaining);
}
/**
* Find student, degree, then perform set difference.
* Then, for every course in that set difference check if the student's courses
* meet its prereqs. If so, print.
* Set difference is O(nlogn) (loop then lookup in binary tree)
* Assume O(n/10) = O(n) courses in set difference.
*
* Find course is O(n) so we are currently at O(n^2) Then we have to try to find all of
* them in the binary tree so that is O(log(n))
* summing up to O(n^2 log(n))
*
* With pointers we would have an O(nlog(n)) as we would save the O(n) course look up.
*/
void Graph_describeNextDegreeReqs(Graph *graph, char* studentName) {
Student *student = ArrayList_find(graph->students, studentName, Student_compareString);
if (student == NULL) {
printf("NOT FOUND\n");
return;
}
Degree *degree = ArrayList_find(graph->degrees, student->degree, Degree_compareString);
if (degree == NULL) {
printf("ERROR, DEGREE NOT FOUND.\n");
return;
}
Requirements *remaining = malloc(sizeof(Requirements));
Requirements_init(remaining);
Requirements_findDifference(degree->reqs, student->courses, remaining);
/**
* each currLine is a LinkedList. The loop then looks again at each element.
*/
for (Node *currLine = remaining->ands->head; currLine != NULL;currLine = currLine->next) {
LinkedList *courseList = (LinkedList *) currLine->data;
for (Node *currCourse = courseList->head; currCourse != NULL; currCourse = currCourse->next) {
bool canTake = true;
/**
* Find the course from the set difference.
*/
Course *course = Graph_findCourse(graph, currCourse->data);
/**
* Now look at all of the sttudents courses and try to see if prereqs are met.
*/
for (Node *curr = course->prereqs->ands->head; curr != NULL; curr = curr->next) {
/**
* The comparator handles the OR requirement. This for loop handles
* the AND requirement. All prereqs need to be met for it to be printed.
*/
LinkedList *ors = (LinkedList *) curr->data;
char *found = LinkedList_find(ors, student->courses, BinaryTree_findComparator);
if (found == NULL) {
canTake = false;
break;
}
}
if (canTake) printf("%s\n", course->name);
}
}
Requirements_free(remaining, dont_free);
free(remaining);
}
/**
* Remove the course first from the department, O(log(n)), then from degrees O(n)
* then from all courses' prereqs O(pn) Since prereqs are small,
* it is essentially O(n)
*/
void Graph_removeCourse(Graph *graph, char *line) {
if (line == NULL) return;
char *str;
// First, parse department
str = strtok(line, ",");
if (str == NULL) {
printf("ERROR REMOVING COURSE\n");
return;
}
Department *department = ArrayList_find(graph->departments, str, Department_compareString);
if (department == NULL) {
printf("CANNOT REMOVE COURSE. DEPARTMENT NOT FOUND\n");
return;
}
// Next, parse course name and title
str = strtok(NULL, ",");
if (str == NULL) {
printf("ERROR REMOVING COURSE\n");
return;
}
str++;
Course *course = BinaryTree_find(department->courses, str);
if (course == NULL) {
printf("Course not in Department\n");
} else {
BinaryTree_remove(department->courses, str, Department_courseFree);
}
// Remove from Degrees
for (size_t i = 0; i < graph->degrees->size; i++) {
Degree_removeCourse(ArrayList_get(graph->degrees, i), str);
}
// Remove from prereqs
Course *parent;
for (size_t i = 0; i < graph->departments->size; i++) {
department = ArrayList_get(graph->departments, i);
ArrayList *list = malloc(sizeof(ArrayList));
ArrayList_init(list);
BinaryTree_serialize(department->courses, list);
for (size_t j = 0; j < list->size; j++) {
parent = ArrayList_get(list, j);
Requirements_removeCourse(parent->prereqs, str);
}
ArrayList_free(list, dont_free);
free(list);
}
}
/**
* Goes through every possible course and checks if it has a prereq with
* courseName. If so, prints the parent course out. Also prints the degrees
* associated with courseName
*
* O(a d n) + O(dn) where n is the number of courses. Note that since the number
* of prereqs is always low (1-3), and so is degrees it is just O(n) + O(n) = O(n)
*/
void Graph_describeCourseEffect(Graph *graph, char *courseName) {
Course *course = Graph_findCourse(graph, courseName);
if (course == NULL) {
printf("NOT FOUND\n");
return;
}
Department *department;
bool first = true;
/**
* Go through every department.
*/
for (size_t i = 0; i < graph->departments->size; i++) {
department = ArrayList_get(graph->departments, i);
/**
* Go through every course and check if this is a prereq for any of them.
* Use special comparator to check, and print it if it is.
*/
ArrayList *list = malloc(sizeof(ArrayList));
ArrayList_init(list);
BinaryTree_serialize(department->courses, list);
for (size_t j = 0; j < list->size; j++) {
Course *parent = ArrayList_get(list, j);
/**
* Comparator finds the course in the list without needing another for loop
*/
Course *foundPrereq = LinkedList_find(parent->prereqs->ands, course->name,
Requirements_innerListFindComparator);
if (foundPrereq != NULL) {
if (!first) {
printf(", "); // Ensures proper delim placement.
}
first = false;
printf("%s", parent->name);
}
}
ArrayList_free(list, dont_free);
free(list);
}
if (first == true) printf("No courses have %s as a pre-requisite", courseName);
// Many courses could have it as prereq
char str[MAX_LINE_LENGTH * 2], delim[] = "\n";
Graph_findDegreesStr(graph, courseName, str, delim);
printf("\n%s\n", str);
}
/**
* First find the degree and course then remove it from the degree
* Should be O(n) to find the course and O(1) to find the degree cause there are only
* a few degrees.
*/
void Graph_removeCourseDegree(Graph *graph, char *line) {
if (line == NULL) return;
char *str;
// First, parse department
str = strtok(line, ",");
if (str == NULL) {
printf("ERROR REMOVING COURSE\n");
return;
}
Degree *degree = ArrayList_find(graph->degrees, str, Degree_compareString);
if (degree == NULL) {
printf("CANNOT REMOVE COURSE. DEGREE NOT FOUND\n");
return;
}
// Next, parse course name and title
str = strtok(NULL, ",");
if (str == NULL) {
printf("ERROR REMOVING COURSE\n");
return;
}
str++;
Degree_removeCourse(degree, str);
}
/**
* Print the title and prereqs
*/
void Graph_describeCourse(Graph *graph, char *courseName) {
Course *course = Graph_findCourse(graph, courseName);
if (course == NULL) {
printf("NOT FOUND\n");
return;
}
printf("%s\n", course->title);
char line[MAX_LINE_LENGTH], delim[] = ", ";
Requirements_toString(course->prereqs, line, delim);
printf("%s\n", line);
}
/**
* Print the reqs
*/
void Graph_describeDegree(Graph *graph, char *degreeName) {
Degree *degree = ArrayList_find(graph->degrees, degreeName, Degree_compareString);
if (degree == NULL) {
printf("NOT FOUND\n");
return;
}
char courses[LIST_LENGTH]; // List of Max line lengths
Degree_toString(degree, courses);
printf("%s", courses);
}
/**
* Find the department and degrees first, print them and then print the
* course prereqs in a single line
*/
void Graph_printCourse(Graph *graph, char *courseName) {
Course *course = Graph_findCourse(graph, courseName);
if (course == NULL) {
printf("NOT FOUND\n");
return;
}
Department *department = Graph_findDepartmentOfCourse(graph, courseName);
printf("department: %s\n", department->name);
char str[MAX_LINE_LENGTH * 2], delim[] = ", ";
Graph_findDegreesStr(graph, courseName, str, delim);
printf("degree: %s\n", str);
if (course->prereqs->ands->size == 0) {
strcpy(str, "NONE");
} else {
Requirements_toString(course->prereqs, str, delim);
}
printf("pre-requisites: %s\n", str);
}
/**
* Print the reqs
*/
void Graph_printDegree(Graph *graph, char *degreeName) {
Degree *degree = ArrayList_find(graph->degrees, degreeName, Degree_compareString);
if (degree == NULL) {
printf("NOT FOUND\n");
return;
}
char str[LIST_LENGTH]; // List of max line lengths
Degree_toString(degree, str);
printf("%s", str);
}
/**
* Print every course by calling Course_toString on all of them
*/
void Graph_printDepartment(Graph *graph, char *departmentName) {
Department *department = ArrayList_find(graph->departments, departmentName, Department_compareString);
if (department == NULL) {
printf("NOT FOUND\n");
return;
}
char str[2 * LIST_LENGTH]; // List of 2* max line lengths
Department_toString(department, str);
printf("%s", str);
}
void Graph_printStudent(Graph* graph, char *studentName) {
Student *student = ArrayList_find(graph->students, studentName, Student_compareString);
if (student == NULL) {
printf("NOT FOUND\n");
return;
}
char str[LIST_LENGTH];
Student_toString(student, str);
printf("%s", str);
}
void Graph_print(Graph *graph) {
printf("Printing Graph.\n");
char *str = malloc(32 * LIST_LENGTH); // Debugging, can include every file read 256KB
ArrayList_toString(graph->departments, Department_toString, str);
printf("Departments.\n");
printf("%s", str);
ArrayList_toString(graph->degrees, Degree_toString, str);
printf("Degrees.\n");
printf("%s", str);
ArrayList_toString(graph->students, Student_toString, str);
printf("Students.\n");
printf("%s", str);
free(str);
}
/**
* Call each of the the wrapper frees for the Arraylists then free the lists themselves
*/
void Graph_free(Graph *graph) {
ArrayList_free(graph->departments, Graph_departmentFree);
ArrayList_free(graph->degrees, Graph_degreeFree);
ArrayList_free(graph->students, Graph_studentFree);
free(graph->departments);
free(graph->degrees);
free(graph->students);
graph->students = NULL;
graph->degrees = NULL;
graph->departments = NULL;
}
void Graph_departmentFree(void *data) {
Department_free(data);
free(data);
}
void Graph_studentFree(void *data) {
Student_free(data);
free(data);
}
void Graph_degreeFree(void *data) {
Degree_free(data);
free(data);
}