begin refactor

This commit is contained in:
SowinskiBraeden committed 2025-07-24 22:34:48 -07:00
1 parent b904ca8535
commit 969557cea5
10 files changed
+322 -444

No files matched your search

+3 -1
View File
@@ -2,9 +2,11 @@
.vscode/
# testing
*.py
*test.c
*.out
# output
output/
obj/
bin/
+3
View File
@@ -28,4 +28,7 @@ $(BIN_DIR) $(OBJ_DIR):
clean:
@$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
run:
@$(BIN_DIR)/main
-include $(OBJ:.o=.d)
+1 -1
View File
@@ -33,7 +33,7 @@
static const char FLEX[][11] = {"XAT--12A-S", "XAT--12B-S"};
void handle(void* mem, char name[]);
void handleMalloc(void* mem, char name[]);
bool isFlex(char crsNo[MAX_COURSE_NO_LEN]);
+1 -1
View File
@@ -28,7 +28,7 @@ UNIQUE_COURSES getNumberOfCourses(CSV_LINE *lines, size_t lines_len) {
COURSE *getCourses(CSV_LINE *lines, size_t lines_len, UNIQUE_COURSES unique_course_info) {
COURSE *courses = malloc(unique_course_info.numberOfCourses * sizeof(COURSE));
handle(courses, "'courses' from getCourses");
handleMalloc(courses, "'courses' from getCourses");
for (size_t i = 0; i < unique_course_info.numberOfCourses; i++) {
for (size_t j = 0; j < lines_len; j++) {
if (strcmp(lines[j].crsNo, unique_course_info.uniqueCrsNos[j]) == 0) {
+1 -1
View File
@@ -36,7 +36,7 @@ CSV_LINE *csvReader(char data_dir[], size_t size) {
}
CSV_LINE *lines = malloc(size * sizeof(CSV_LINE));
handle(lines, "'lines' from csvReader");
handleMalloc(lines, "'lines' from csvReader");
char buff[MAX_CHAR];
int i = 0;
+286 -435
View File
@@ -8,11 +8,14 @@
#include "../include/courses.h"
#include "../include/generator.h"
/*** for testing ***/
#include<stdio.h>
const char hex[] = "0123456789abcdefABCDEF";
void appendChar(char *str, char ch) {
void appendChar(char *str, char c) {
int len = strlen(str);
str[len] = ch;
str[len] = c;
str[len + 1] = '\0';
}
@@ -39,46 +42,45 @@ uint8_t *equal(uint8_t *arr, size_t size) {
}
int stepIndex(int offset, StepType type, uint8_t blocksPerSemester) {
if (type == FirstToSecondSemester)
return (offset == 0 || offset == -1 * (blocksPerSemester - 1)) ? blocksPerSemester : (-1 * (blocksPerSemester - 1));
if (type == FirstToSecondSemester) {
bool toNextSemester = offset == 0 || offset == -1 * (blocksPerSemester - 1);
return toNextSemester ? blocksPerSemester : (-1 * (blocksPerSemester - 1));
}
if (type == SecondToFirstSemester)
return (offset == 0 || offset == blocksPerSemester + 1) ? (-1 * blocksPerSemester) : (blocksPerSemester + 1);
if (type == SecondToFirstSemester) {
bool toLastSemester = offset == 0 || offset == blocksPerSemester + 1;
return toLastSemester ? (-1 * blocksPerSemester) : (blocksPerSemester + 1);
}
// Should not be possible
return -1;
}
TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *courses, size_t size_courses) {
TIMETABLE_BLOCK defaultBlock;
defaultBlock.numberOfClasses = 0;
TIMETABLE timetable = {{defaultBlock}, false};
/*
totals student requests for each course,
if course requests > MIN_REQ, course ID
is added to activeCourses.
*/
uint16_t calculateActiveCourses(
STUDENT *students,
size_t size_students,
COURSE *courses,
size_t size_courses,
char **activeCourses,
uint16_t *activeCoursesIndexes
) {
uint8_t MEDIAN = floor((float) (MIN_REQ + CLASS_CAP) / 2);
uint8_t BLOCKS_PER_SEMESTER = TOTAL_BLOCKS / 2;
uint16_t activeCoursesLen = 0;
/*** STEP 1 - Tally requests to check which courses are eligable to run ***/
char **activeCourses = malloc(MAX_CLASSES * sizeof(char *));
uint16_t *activeCoursesIndexes = malloc(MAX_CLASSES * sizeof(uint16_t));
handle(activeCourses, "'activeCourses' from generateTimetable");
handle(activeCoursesIndexes, "'activeCoursesIndexes' from generateTimetable");
uint16_t activeCoursesLen = 0; // also acts as the length of activeCourses
for (size_t i = 0; i < size_students; i++) {
for (size_t j = 0; j < students[i].requestsLen; j++) {
if (students[i].requests[j].alternate || isFlex(students[i].requests[j].crsNo)) continue;
if (students[i].requests[j].alternate ||
isFlex(students[i].requests[j].crsNo)) continue;
for (size_t k = 0; k < size_courses; k++) {
if (strcmp(courses[k].crsNo, students[i].requests[j].crsNo) == 0) {
courses[k].requests++;
if (courses[k].requests >= MIN_REQ) {
if (activeCoursesLen == 0) {
activeCourses[activeCoursesLen] = malloc(sizeof(char) * MAX_COURSE_NO_LEN);
handle(activeCourses[activeCoursesLen], "'activeCourses[idx]' from generateTimetable");
strcpy(activeCourses[activeCoursesLen], courses[k].crsNo);
activeCoursesIndexes[activeCoursesLen] = k;
activeCoursesLen++;
} else {
bool exists = false;
for (size_t l = 0; l < activeCoursesLen; l++) {
if (strcmp(activeCourses[l], courses[k].crsNo) == 0) {
@@ -89,472 +91,321 @@ TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *cou
if (!exists) {
activeCourses[activeCoursesLen] = malloc(sizeof(char) * MAX_COURSE_NO_LEN);
handle(activeCourses[activeCoursesLen], "'activeCourses[idx]' from generateTimetable");
handleMalloc(activeCourses[activeCoursesLen], "'activeCourses[idx]' from calculateActiveCourses");
strcpy(activeCourses[activeCoursesLen], courses[k].crsNo);
activeCoursesIndexes[activeCoursesLen] = k;
activeCoursesIndexes[activeCoursesLen] = k; // Save original index
activeCoursesLen++;
}
}
}
break;
}
}
}
}
/*** STEP 2 - Generate classes with no students, but calculate the number of expected students per class ***/
uint8_t *allClassRunCounts = malloc(activeCoursesLen * sizeof(uint8_t));
// max this out to total number of classrooms available between both semesters
CLASS *classes = malloc(CLASSROOMS * TOTAL_BLOCKS * sizeof(CLASS));
handle(allClassRunCounts, "'allClassRunCounts' from generateTimetable");
handle(classes, "'classes' from generateTimetable");
size_t classesLen = 0;
for (size_t i = 0; i < activeCoursesLen; i++) {
uint16_t index = activeCoursesIndexes[i];
uint8_t classRunCount = floor((float) courses[index].requests / MEDIAN);
uint8_t remaining = courses[index].requests % MEDIAN;
// add 1 to classRunCount in case we need to create an extra class with remaining
size_t *courseClassIndexes = malloc((classRunCount + 1) * sizeof(size_t));
handle(courseClassIndexes, "'courseClassIndexes' from generateTimetable");
for (size_t j = 0; j < classRunCount; j++) {
CLASS newClass;
strcpy(newClass.baseCrsNo, courses[index].crsNo);
char courseID[MAX_COURSE_ID_LEN];
strcpy(courseID, courses[index].crsNo);
appendChar(courseID, hex[j]);
strcpy(newClass.crsNo, courseID);
strcpy(newClass.description, courses[index].description);
newClass.numberOfStudents = MEDIAN; // The expected number of students in this class
classes[classesLen] = newClass;
courseClassIndexes[j] = classesLen;
classesLen++;
return activeCoursesLen;
}
//*** Handle remaining requests ***/
void createAndInsertClass(
CLASS *classes,
size_t *classesLen,
size_t *courseClassIndexes,
size_t index,
COURSE course,
char id,
uint8_t numberOfStudents
) {
CLASS newClass;
strcpy(newClass.baseCrsNo, course.crsNo);
// Can we add remaining requests to existing classes
bool remainingFitsInExistingClasses = remaining <= classRunCount * (CLASS_CAP - MEDIAN);
char courseID[MAX_COURSE_ID_LEN];
strcpy(courseID, course.crsNo);
appendChar(courseID, id);
strcpy(newClass.crsNo, courseID);
// Can we create a new class using only remaining requests
bool remainingCanCreateNewClass = remaining >= MIN_REQ;
strcpy(newClass.description, course.description);
// Can we create a new class if we borrow students from created classes to add to remaining requests to meet min req
bool remainingPlusExtraFromExistingCanCreateNewClass = MIN_REQ - remaining < classRunCount * (MEDIAN - MIN_REQ);
newClass.numberOfStudents = numberOfStudents;
if (remainingFitsInExistingClasses) {
// Simply add remaining to existing classes
classes[*classesLen] = newClass;
courseClassIndexes[index] = *classesLen;
(*classesLen)++;
}
void addRemainingToClasses(
uint8_t classRunCount,
uint8_t remaining,
CLASS *classes,
size_t *courseClassIndexes
) {
while (remaining > 0) {
for (size_t j = 0; j < classRunCount; j++) {
classes[courseClassIndexes[j]].numberOfStudents++;
for (size_t i = 0; i < classRunCount; i++) {
size_t index = courseClassIndexes[i];
classes[index].numberOfStudents++;
remaining--;
if (remaining == 0) break;
}
}
}
} else if (remainingCanCreateNewClass) {
// Create new class
CLASS newClass;
strcpy(newClass.baseCrsNo, courses[index].crsNo);
char courseID[MAX_COURSE_ID_LEN];
strcpy(courseID, courses[index].crsNo);
appendChar(courseID, hex[classRunCount]);
strcpy(newClass.crsNo, courseID);
strcpy(newClass.description, courses[index].description);
newClass.numberOfStudents = remaining;
// Insert class into empty classes array and update index
classes[classesLen] = newClass;
courseClassIndexes[classRunCount] = classesLen;
classesLen++;
// update class run count; if there is more than one class, equalize the class number of students
classRunCount++;
if (classRunCount >= 2) {
void equalizeNumberOfStudents(
CLASS *classes,
size_t *courseClassIndexes,
uint8_t classRunCount
) {
uint8_t *numberOfStudentsArr = malloc(classRunCount * sizeof(uint8_t));
handle(numberOfStudentsArr, "'numberOfStudentsArr' from generateTimetable");
for (size_t j = 0; j < classRunCount; j++)
numberOfStudentsArr[j] = classes[courseClassIndexes[j]].numberOfStudents;
handleMalloc(numberOfStudentsArr, "'numberOfStudentsArr' from equalizeNumberOfStudents");
for (size_t i = 0; i < classRunCount; i++) {
size_t index = courseClassIndexes[i];
numberOfStudentsArr[i] = classes[index].numberOfStudents;
}
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
for (size_t j = 0; j < classRunCount; j++)
classes[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
for (size_t i = 0; i < classRunCount; i++) {
size_t index = courseClassIndexes[i];
classes[index].numberOfStudents = numberOfStudentsArr[i];
}
free(numberOfStudentsArr);
}
} else if (remainingPlusExtraFromExistingCanCreateNewClass) {
// Take 1 student from each existing class till min requirement is met
void createClassWithRemaining(
COURSE course,
uint8_t *classRunCount,
uint8_t remaining,
CLASS *classes,
size_t *classesLen,
size_t *courseClassIndexes
) {
createAndInsertClass(
classes,
classesLen,
courseClassIndexes,
*classRunCount,
course,
hex[*classRunCount],
remaining
);
// Update class run count; if there is more than one class, equalize the class number of students
(*classRunCount)++;
if (*classRunCount >= 2)
equalizeNumberOfStudents(classes, courseClassIndexes, *classRunCount);
}
void createClassWithRemainingAndExisting(
COURSE course,
uint8_t *classRunCount,
uint8_t remaining,
CLASS *classes,
size_t *classesLen,
size_t *courseClassIndexes
) {
// Take 1 student spot from each existing class till
// minimum requirement is met
while (remaining < MIN_REQ) {
for (size_t j = 0; j < classRunCount; j++) {
classes[courseClassIndexes[j]].numberOfStudents--;
for (size_t i = 0; i < *classRunCount; i++) {
size_t index = courseClassIndexes[i];
classes[index].numberOfStudents--;
remaining++;
if (remaining == MIN_REQ) break;
}
}
// Create new class with remaining
CLASS newClass;
strcpy(newClass.baseCrsNo, courses[index].crsNo);
char courseID[MAX_COURSE_ID_LEN];
strcpy(courseID, courses[index].crsNo);
appendChar(courseID, hex[classRunCount]);
strcpy(newClass.crsNo, courseID);
strcpy(newClass.description, courses[index].description);
newClass.numberOfStudents = remaining;
createAndInsertClass(
classes,
classesLen,
courseClassIndexes,
*classRunCount,
course,
hex[*classRunCount],
remaining
);
// Insert class into empty classes array and update index
classes[classesLen] = newClass;
courseClassIndexes[classRunCount] = classesLen;
classesLen++;
classRunCount++;
// Equalize the class number of students
uint8_t *numberOfStudentsArr = malloc(classRunCount * sizeof(uint8_t));
handle(numberOfStudentsArr, "'numberOfStudentsArr' from generateTimetable");
for (size_t j = 0; j < classRunCount; j++)
numberOfStudentsArr[j] = classes[courseClassIndexes[j]].numberOfStudents;
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
for (size_t j = 0; j < classRunCount; j++)
classes[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
free(numberOfStudentsArr);
} else {
/*
If all above cannot handle remaining requests we will add as many of
the remaining requests to the existing classes. Any number of requests
that dont fit will be ignored so later they can be folded into their
alternative choices
*/
(*classRunCount)++;
equalizeNumberOfStudents(classes, courseClassIndexes, *classRunCount);
}
void addRemainingToClassesTillFull(
size_t classRunCount,
size_t *courseClassIndexes,
CLASS *classes,
size_t remaining
) {
bool full = false;
while (!full) {
for (size_t j = 0; j < classRunCount; j++) {
if (classes[courseClassIndexes[classRunCount - 1]].numberOfStudents == CLASS_CAP) {
// If the last class in the array is at class_cap, all other classes must be at class cap and we are full
for (size_t i = 0; i < classRunCount; i++) {
size_t lastIndex = courseClassIndexes[classRunCount - 1];
if (classes[lastIndex].numberOfStudents == CLASS_CAP) {
// If the last class is full, all classes before are
// assumed to be full ass well.
full = true;
break;
}
classes[courseClassIndexes[j]].numberOfStudents++;
size_t index = courseClassIndexes[i];
if (classes[index].numberOfStudents == CLASS_CAP) continue;
classes[index].numberOfStudents++;
remaining--;
}
}
}
/*
Creates an array of classes,
each with an estimaed number of students
per class.
*/
size_t generateEmptyClasses(
COURSE *courses,
size_t size_courses,
uint16_t *activeCoursesIndexes,
uint16_t activeCoursesLen,
uint8_t *allClassRuns,
CLASS *classes,
uint8_t MEDIAN
) {
size_t classesLen = 0;
for (size_t i = 0; i < activeCoursesLen; i++) {
uint16_t index = activeCoursesIndexes[i];
uint8_t classRunCount = floor((float) courses[index].requests / MEDIAN);
uint8_t remaining = courses[index].requests % MEDIAN;
// Add 1 to classRunCount in case an extra class can be created with remaining
size_t *courseClassIndexes = malloc((classRunCount + 1) * sizeof(size_t));
handleMalloc(courseClassIndexes, "'courseClassIndexes' from generateEmptyClasses");
for (size_t j = 0; j < classRunCount; j++) {
createAndInsertClass(
classes,
&classesLen,
courseClassIndexes,
j,
courses[index],
hex[j],
MEDIAN
);
}
/*** Handle remaining requests ***/
// Can add remaining requests to existing classes
bool remainingFitsInExisting = remaining <= classRunCount * (CLASS_CAP - MEDIAN);
// Can create new class using only remaining requets
bool remainingCanCreateNewClass = remaining >= MIN_REQ;
// Can create new class with remaining and borrowing requests from existing classes
bool remainingWithExistingCanCreateNewClass = MIN_REQ - remaining < classRunCount * (MEDIAN - MIN_REQ);
if (remainingFitsInExisting) {
addRemainingToClasses(classRunCount, remaining, classes, courseClassIndexes);
} else if (remainingCanCreateNewClass) {
createClassWithRemaining(
courses[index],
&classRunCount,
remaining,
classes,
&classesLen,
courseClassIndexes
);
} else if (remainingWithExistingCanCreateNewClass) {
createClassWithRemainingAndExisting(
courses[index],
&classRunCount,
remaining,
classes,
&classesLen,
courseClassIndexes
);
} else {
addRemainingToClassesTillFull(
classRunCount,
courseClassIndexes,
classes,
remaining
);
}
free(courseClassIndexes);
allClassRunCounts[i] = classRunCount;
allClassRuns[i] = classRunCount;
}
// realloc classes to correct size
CLASS *tempClasses = malloc(classesLen * sizeof(CLASS));
handle(tempClasses, "'tempClasses' from generateTimetable");
for (size_t i = 0; i < classesLen; i++)
tempClasses[i] = classes[i];
classes = realloc(classes, classesLen * sizeof(CLASS));
memcpy(classes, tempClasses, classesLen * sizeof(CLASS));
free(tempClasses);
// CLASS *tempClasses = malloc(classesLen * sizeof(CLASS));
// handleMalloc(tempClasses, "'tempClasses' from generateEmptyClasses");
// for (size_t i = 0; i < classesLen; i++)
// tempClasses[i] = classes[i];
// classes = realloc(classes, classesLen * sizeof(CLASS));
// memcpy(classes, tempClasses, classesLen * sizeof(CLASS));
// free(tempClasses);
// ^^^ not needed if we are iterating using classesLen
// realloc is an expensive operation apparently.
return classesLen;
}
TIMETABLE generateTimetable(
STUDENT *students,
size_t size_students,
COURSE *courses,
size_t size_courses
) {
TIMETABLE_BLOCK defaultBlock;
defaultBlock.numberOfClasses = 0;
TIMETABLE timetable = {{defaultBlock}, false};
uint8_t MEDIAN = floor((float) (MIN_REQ + CLASS_CAP) / 2);
uint8_t BLOCKS_PER_SEMESTER = TOTAL_BLOCKS / 2;
/*** STEP 1 - Tally requests to check which coures are eligble to run ***/
// Array of course IDs that have met minimum requests.
char **activeCourses = malloc(MAX_CLASSES * sizeof(char *));
uint16_t *activeCoursesIndexes = malloc(MAX_CLASSES * sizeof(uint16_t));
handleMalloc(activeCourses, "'activeCourses' from generateTimetable");
handleMalloc(activeCoursesIndexes, "'activeCoursesIndexes' from generateTimetable");
uint16_t activeCoursesLen = calculateActiveCourses(
students,
size_students,
courses,
size_courses,
activeCourses,
activeCoursesIndexes
);
/*** STEP 2 - Generate classes with no students
* - Calculate number of expected students per class ***/
uint8_t *allClassRuns = malloc(activeCoursesLen * sizeof(uint8_t));
CLASS *classes = malloc(CLASSROOMS * TOTAL_BLOCKS * sizeof(CLASS));
handleMalloc(allClassRuns, "'allClassRuns' from generateTimetable");
handleMalloc(classes, "'classes' from generateTimetable");
size_t size_classes = generateEmptyClasses(
courses,
size_courses,
activeCoursesIndexes,
activeCoursesLen,
allClassRuns,
classes,
MEDIAN
);
free(activeCoursesIndexes);
/*** STEP 3 - Insert students into empty classes ***/
STUDENT *tempStudents = malloc(size_students * sizeof(STUDENT));
handle(tempStudents, "'tempStudents' from generateTimetable");
size_t size_tempStudents = size_students;
memcpy(tempStudents, students, size_students * sizeof(STUDENT));
uint8_t *currentInserted = malloc(classesLen * sizeof(uint8_t));
handle(currentInserted, "'currentInserted' from generateTimetable");
for (size_t i = 0; i < classesLen; i++)
currentInserted[i] = 0;
while (size_tempStudents > 0) {
// Choose student at random, to prevent success bias to students first in the array
STUDENT student = tempStudents[rand() % size_tempStudents];
// Create an array of students alternates
size_t numberOfAlts = 0;
for (size_t i = 0; i < student.requestsLen; i++)
if (student.requests[i].alternate)
numberOfAlts++;
REQUEST *alternates = malloc(numberOfAlts * sizeof(REQUEST));
handle(alternates, "'alternates' from generateTimetable");
size_t alternateIdx = 0;
for (size_t i = 0; i < student.requestsLen; i++) {
if (student.requests[i].alternate) {
alternates[alternateIdx] = student.requests[i];
alternateIdx++;
}
}
// Search existing classes to insert student based off request
for (size_t i = 0; i < student.requestsLen; i++) {
if (student.requests[i].alternate) continue; // Ignore alternates
char course[MAX_COURSE_NO_LEN] = {"\0"};
strcpy(course, student.requests[i].crsNo);
bool getAvailableCourse = true;
bool isAlt = false;
while (getAvailableCourse) {
for (size_t j = 0; j < classesLen; j++) {
// Class exists in classes
if (strcmp(classes[j].baseCrsNo, course) == 0) {
// If this is an alternate, and there is room to expand, increase number of students to allow extra
if (isAlt && classes[j].numberOfStudents < CLASS_CAP)
classes[j].numberOfStudents++;
// Class exists with room for student
if (currentInserted[j] < classes[j].numberOfStudents) {
classes[j].students[currentInserted[j]] = student.pupilNum;
student.classes++;
currentInserted[j]++;
getAvailableCourse = false;
break;
} else if (currentInserted[j] == classes[j].numberOfStudents) {
// If class is full, and there's no more classes available for that course, convert to alt
if (j == classesLen - 1 || (j != classesLen - 1 && strcmp(classes[j + 1].baseCrsNo, course) != 0)) {
if (numberOfAlts > 0) {
// Use alternate
strcpy(course, alternates[0].crsNo); // asign alternate to course and retry
// remove alternate from array of alts to retry same alt over and over
REQUEST *tempAlternates = malloc(numberOfAlts * sizeof(REQUEST));
handle(tempAlternates, "'tempAlternates' from generateTimetable");
memcpy(tempAlternates, alternates, numberOfAlts * sizeof(REQUEST));
numberOfAlts--;
alternates = realloc(alternates, numberOfAlts * sizeof(REQUEST));
for (size_t k = 1; k <= numberOfAlts; k++)
alternates[k - 1] = tempAlternates[k];
free(tempAlternates);
isAlt = true;
break;
} else {
// Force break the loop, ignore as it cannot be resolved
// Allow administrator to handle error manually
getAvailableCourse = false;
break;
}
}
}
// this class does not exist, i.e not enough requests
} else if (j == classesLen - 1) {
if (numberOfAlts > 0) {
// Use alternate
strcpy(course, alternates[0].crsNo); // asign alternate to course and retry
// remove alternate from array of alts to retry same alt over and over
REQUEST *tempAlternates = malloc(numberOfAlts * sizeof(REQUEST));
handle(tempAlternates, "'tempAlternates' from generateTimetable");
memcpy(tempAlternates, alternates, numberOfAlts * sizeof(REQUEST));
numberOfAlts--;
alternates = realloc(alternates, numberOfAlts * sizeof(REQUEST));
for (size_t k = 1; k <= numberOfAlts; k++)
alternates[k - 1] = tempAlternates[k];
free(tempAlternates);
isAlt = true;
break;
} else {
// Force break the loop, ignore as it cannot be resolved
// Allow administrator to handle error manually
getAvailableCourse = false;
break;
}
}
}
}
}
// Asign remaining alternates && classes to student
for (size_t i = 0; i < size_students; i++) {
if (students[i].pupilNum == student.pupilNum) {
students[i].remainingAlts = realloc(students[i].remainingAlts, numberOfAlts * sizeof(REQUEST));
memcpy(students[i].remainingAlts, alternates, numberOfAlts * sizeof(REQUEST));
students[i].remainingAltsLen = numberOfAlts;
students[i].classes = student.classes;
break;
}
}
free(alternates);
// realloc tempStudents to be size_tempStudents - 1 without struct of student just processed
size_tempStudents--;
STUDENT *new_tempStudents = malloc(size_tempStudents * sizeof(STUDENT));
handle(new_tempStudents, "'new_tempStudents' from generateTimetable");
size_t idx = 0;
for (size_t i = 0; i <= size_tempStudents; i++) {
if (tempStudents[i].pupilNum != student.pupilNum) {
new_tempStudents[idx] = tempStudents[i];
idx++;
}
}
tempStudents = realloc(tempStudents, size_tempStudents * sizeof(STUDENT));
memcpy(tempStudents, new_tempStudents, size_tempStudents * sizeof(STUDENT));
free(new_tempStudents);
}
free(currentInserted);
free(tempStudents);
/*** STEP 4 - Insert classes into timetable ***/
while (activeCoursesLen > 0) {
// Find highest resource class (most times run)
size_t index = 0;
uint8_t max = 0;
for (size_t i = 0; i < activeCoursesLen; i++) {
if (allClassRunCounts[i] > max) {
max = allClassRunCounts[i];
index = i;
}
}
// Tally first semester and second semester
uint8_t allSemesterBlockLens[TOTAL_BLOCKS] = {0};
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++)
allSemesterBlockLens[i] = timetable.blocks[i].numberOfClasses;
// If there is more than one class running
if (allClassRunCounts[index] > 1) {
// Get index of block with least class run counts
uint8_t minClassrooms = CLASSROOMS;
uint8_t blockIndex = 0;
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++) {
if (allSemesterBlockLens[i] < minClassrooms) {
minClassrooms = allSemesterBlockLens[i];
blockIndex = i;
}
}
StepType step = blockIndex < BLOCKS_PER_SEMESTER ? FirstToSecondSemester : SecondToFirstSemester;
int offset = 0;
// Disperse classes throughout both semesters
// Find base class ID index
size_t indexOffset = 0;
size_t baseIndex = 0;
for (size_t i = 0; i < classesLen; i++) {
if (strcmp(classes[i].baseCrsNo, activeCourses[index]) == 0) {
baseIndex = i;
break;
}
}
uint8_t classRunCounts = allClassRunCounts[index];
for (size_t i = 0; i < classRunCounts; i++) {
bool classInserted = false;
while (!classInserted) {
blockIndex += offset;
// Insert class
if (timetable.blocks[blockIndex].numberOfClasses < CLASSROOMS) {
uint8_t classIndex = timetable.blocks[blockIndex].numberOfClasses;
timetable.blocks[blockIndex].classes[classIndex] = classes[baseIndex + indexOffset];
timetable.blocks[blockIndex].numberOfClasses++;
allClassRunCounts[index]--;
indexOffset++;
classInserted = true;
}
// Update offset and to get index of block for next semester
offset = stepIndex(offset, step, BLOCKS_PER_SEMESTER);
if (blockIndex >= (TOTAL_BLOCKS - 1)) {
blockIndex = step == FirstToSecondSemester ? 0 : BLOCKS_PER_SEMESTER;
offset = 0;
}
}
}
// If the class only runs once, place in semester with least classes
} else if (allClassRunCounts[index] == 1) {
// Get index of block with least class run counts
uint8_t minClassrooms = CLASSROOMS;
uint8_t blockIndex = 0;
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++) {
if (allSemesterBlockLens[i] < minClassrooms) {
minClassrooms = allSemesterBlockLens[i];
blockIndex = i;
}
}
// Get class ID index
size_t baseIndex = 0;
for (size_t i = 0; i < classesLen; i++) {
if (strcmp(classes[i].baseCrsNo, activeCourses[index]) == 0) {
baseIndex = i;
break;
}
}
// Insert class
uint8_t classIndex = timetable.blocks[blockIndex].numberOfClasses;
timetable.blocks[blockIndex].classes[classIndex] = classes[baseIndex];
timetable.blocks[blockIndex].numberOfClasses++;
allClassRunCounts[index]--;
}
// Now that the classRunCount is 0 since all classes have been inserted into the timetable
// delete this course as it has been handled
activeCoursesLen--;
// create temp arrays of data
uint8_t *tempAllClassRunCounts = malloc(activeCoursesLen * sizeof(uint8_t));
char **tempActiveCourses = malloc(activeCoursesLen * sizeof(char*));
handle(tempAllClassRunCounts, "'tempAllClassRunCounts' from generateTimetable");
handle(tempActiveCourses, "'tempActiveCourses' from generateTimetable");
// Copy data without the course we just handled
size_t tempIndex = 0;
for (size_t i = 0; i <= activeCoursesLen; i++) {
if (activeCourses[i] == activeCourses[index]) {
free(activeCourses[i]);
continue;
};
tempAllClassRunCounts[tempIndex] = allClassRunCounts[i];
tempActiveCourses[tempIndex] = activeCourses[i];
tempIndex++;
}
// Reallocate the arrays of course data to be 1 less in size and copy temp data back to them, then free temp data
allClassRunCounts = realloc(allClassRunCounts, activeCoursesLen * sizeof(uint8_t));
activeCourses = realloc(activeCourses, activeCoursesLen * sizeof(char*));
memcpy(allClassRunCounts, tempAllClassRunCounts, activeCoursesLen * sizeof(uint8_t));
memcpy(activeCourses, tempActiveCourses, activeCoursesLen * sizeof(char*));
free(tempAllClassRunCounts);
free(tempActiveCourses);
}
// STEP 5 - fill student schedule
for (size_t i = 0; i < TOTAL_BLOCKS; i++) {
for (size_t j = 0; j < timetable.blocks[i].numberOfClasses; j++) {
for (size_t k = 0; k < timetable.blocks[i].classes[j].numberOfStudents; k++) {
for (size_t l = 0; l < size_students; l++) {
if (students[l].pupilNum == timetable.blocks[i].classes[j].students[k]) {
strcpy(students[l].schedule[i], timetable.blocks[i].classes[j].crsNo);
break;
}
}
}
}
}
// STEP 6 - Solve student schedule errors
/*** Done ***/
free(classes);
free(allClassRunCounts);
free(allClassRuns);
for (size_t i = 0; i < activeCoursesLen; i++)
free(activeCourses[i]);
free(activeCourses);
return timetable;
Executable
+22
View File
@@ -0,0 +1,22 @@
typedef struct {
const char *key;
void *value;
} hashEntry;
struct hashTable {
hashEntry entries;
size_t capacity;
size_t length;
}
hashTable* createHashTable(void) {
hashTable *table = malloc(sizeof(hashTable));
handleMalloc(table, "'table' from createHashTable");
table->length = 0;
table->capacity = 64;
table->entries = calloc(table->capacity, sizeof(hashEntry));
handleMalloc(table->entries, "'table->entries' from createHashTable");
return table;
}
+2 -2
View File
@@ -11,7 +11,7 @@
#include "../include/json.h"
#include "../include/generator.h"
void handle(void *mem, char name[]) {
void handleMalloc(void *mem, char name[]) {
if (mem == NULL) {
fprintf(stderr, "Failed to allocate memory! - Memory: %s\n", name);
exit(-1);
@@ -24,7 +24,7 @@ bool isFlex(char crsNo[MAX_COURSE_NO_LEN]) {
return false;
}
int main(int argc, char **argv) {
int main(void) {
/*
read csv data into array of structs that can
be processed into an array of student structs
+3 -3
View File
@@ -40,7 +40,7 @@ STUDENT *getStudents(CSV_LINE *lines, size_t lines_len, int total_blocks, UNIQUE
// Create our student array with the correct number of students
STUDENT *students = malloc(students_info.numberOfStudents * sizeof(STUDENT));
handle(students, "'students' from getStudents");
handleMalloc(students, "'students' from getStudents");
for (uint16_t i = 0; i < students_info.numberOfStudents; i++) {
STUDENT student;
@@ -52,14 +52,14 @@ STUDENT *getStudents(CSV_LINE *lines, size_t lines_len, int total_blocks, UNIQUE
student.remainingAltsLen = 0;
REQUEST *requests = malloc(numRequests[i] * sizeof(REQUEST));
handle(requests, "'requests' from getStudents");
handleMalloc(requests, "'requests' from getStudents");
student.requests = requests;
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++)
strcpy(student.schedule[i], i < TOTAL_BLOCKS / 2 ? FLEX[0] : FLEX[1]);
REQUEST *remainingAlts = malloc(MAX_REQUEST_ALTS * sizeof(REQUEST));
handle(remainingAlts, "'remainingAlts' from getStudents");
handleMalloc(remainingAlts, "'remainingAlts' from getStudents");
student.remainingAlts = remainingAlts;
students[i] = student;
Executable
BIN
View File
Binary file not shown.