VIAplanner/via-timetable

View on GitHub
src/timetable-planner/index.js

Summary

Maintainability
F
1 mo
Test Coverage
const sortCourseSection = require('./combinations/combinations');

/**
 * Search for the first index of course that has type section
 * @param {*} courseSection
 * @param {*} type
 */
const searchForSectionIndex = (courseSection, type) => {
  let index = -1;
  for (const section of courseSection) {
    if (section[type].length !== 0) {
      index = courseSection.indexOf(section);
      break;
    }
  }
  return index;
};
/**
 * Search for the next index of course that has type section
 * @param {*} courseSection
 * @param {*} type
 * @param {*} prevIndex
 */
const searchForSectionIndexAfterprevIndex = (
  courseSection,
  type,
  prevIndex,
) => {
  let index = -1;
  for (const section of courseSection) {
    const tempI = courseSection.indexOf(section);
    if (section[type].length !== 0 && tempI > prevIndex) {
      index = tempI;
      break;
    }
  }
  return index;
};
/**
 * Adds the section to the timetable
 * @param {*} sections
 * @param {*} timetable
 */
const addSectionToTimetable = (sections, timetable) => {
  for (const section of sections) {
    for (const time of section.times) {
      if (section.sectionCode.length === 3) {
        const timetableSection = {
          code: section.comboCode.substring(0, section.comboCode.length - 3),
          sectionCode: section.sectionCode,
          instructors: section.instructors,
          method: section.method,
          ...time,
        };
        timetable[time.day].push(timetableSection);
      } else {
        const timetableSection = {
          code: section.comboCode.substring(0, section.comboCode.length - 5),
          sectionCode: section.sectionCode,
          instructors: section.instructors,
          method: section.method,
          ...time,
        };
        timetable[time.day].push(timetableSection);
      }
    }
  }
};

const createCopyOfCourseSection = courseSections => {
  const copy = [];
  for (const courseSection of courseSections) {
    const temp = {};
    temp.code = courseSection.code;
    temp.lecture = [...courseSection.lecture];
    temp.practical = [...courseSection.practical];
    temp.tutorial = [...courseSection.tutorial];
    copy.push(temp);
  }
  return copy;
};

/**
 * Make a complete shallow copy of a timetable
 * @param {*} timetable
 */
const createShallowCopyOfTimetable = timetable => {
  const shallowCopy = {
    MONDAY: [],
    TUESDAY: [],
    WEDNESDAY: [],
    THURSDAY: [],
    FRIDAY: [],
  };
  const days = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY'];
  for (const day of days) {
    shallowCopy[day].push(...timetable[day]);
  }
  return shallowCopy;
};

/**
 *
 * Helper function for OverlapExist
 * @param {Timetable} timetable
 * @param {string} day
 * @returns
 */
const checkOverlapForDay = (timetable, day) => {
  let section = 0;
  while (section < timetable[day].length) {
    let section2 = +section + +1;
    while (section2 < timetable[day].length) {
      if (
        (timetable[day][section].start >= timetable[day][section2].start &&
          timetable[day][section].start < timetable[day][section2].end) ||
        (timetable[day][section].end > timetable[day][section2].start &&
          timetable[day][section].end <= timetable[day][section2].end) ||
        (timetable[day][section2].start >= timetable[day][section].start &&
          timetable[day][section2].start < timetable[day][section].end) ||
        (timetable[day][section2].end > timetable[day][section].start &&
          timetable[day][section2].end <= timetable[day][section].end)
      ) {
        return true;
      }
      section2 += 1;
    }
    section += 1;
  }
  return false;
};

/**
 *
 * Checks overlap of course times for each day in a timetable
 * @param {Timetable} timetable
 * @returns {boolean}
 */
const overlapExists = timetable => {
  const days = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY'];
  let exists = false;
  for (const day of days) {
    exists = exists || checkOverlapForDay(timetable, day);
  }
  return exists;
};

/**
 * Eliminates the section of the courses based on the locksections
 * @param {*} courseSections
 * @param {*} lockSection
 */
const lockSectionOfCourse = (courseSections, lockSections) => {
  for (const course of courseSections) {
    for (const section of lockSections) {
      if (course.code === section.slice(0, section.length - 5)) {
        if (section[section.length - 5] === 'L') {
          for (const lecture of course.lecture) {
            if (lecture.sectionCode === section.slice(section.length - 5)) {
              course.lecture = [lecture];
            }
          }
        }
        if (section[section.length - 5] === 'T') {
          for (const tutorial of course.tutorial) {
            if (tutorial.sectionCode === section.slice(section.length - 5)) {
              course.tutorial = [tutorial];
            }
          }
        }
        if (section[section.length - 5] === 'P') {
          for (const practical of course.practical) {
            if (practical.sectionCode === section.slice(section.length - 5)) {
              course.practical = [practical];
            }
          }
        }
      } else if (course.code === section.slice(0, section.length - 3)) {
        if (section[section.length - 3] === 'L') {
          for (const lecture of course.lecture) {
            if (lecture.sectionCode === section.slice(section.length - 3)) {
              course.lecture = [lecture];
            }
          }
        }
      }
    }
  }
};

/**
 * sort course's sections based on the user's preference
 * 0 == in person sections has higher priority
 * 1 == online sections has higher priority
 * 2 == no preference
 */
const sortCourseSections = (course, online) => {
  if (online === 'InPerson') {
    course.lecture.sort((a, b) => (a.sectionCode > b.sectionCode ? 1 : -1));
    course.practical.sort((a, b) => (a.sectionCode > b.sectionCode ? 1 : -1));
    course.tutorial.sort((a, b) => (a.sectionCode > b.sectionCode ? 1 : -1));
  } else if (online === 'Online') {
    course.lecture.sort((a, b) => (a.sectionCode < b.sectionCode ? 1 : -1));
    course.practical.sort((a, b) => (a.sectionCode < b.sectionCode ? 1 : -1));
    course.tutorial.sort((a, b) => (a.sectionCode < b.sectionCode ? 1 : -1));
  }
};

/**
 * sort courses' sections based on the user's preference
 * @param {*} courses
 * @param {*} online
 */
const sortCourses = (courses, online) => {
  for (const course of courses) {
    sortCourseSections(course, online);
  }
  courses.sort((a, b) => (a.practical.length > b.lecture.length ? 1 : -1));
  courses.sort((a, b) => (a.practical.length > b.tutorial.length ? 1 : -1));
  courses.sort((a, b) => (a.practical.length > b.practical.length ? 1 : -1));
};

/**
 *
 * Creates timetable by parse the meetingSections into each day and check for validity
 * @param {MeetingSection[]} fallCourseSection
 * @returns {Timetable}
 */
const createTimetable = (fallCourseSection, winterCourseSection, state) => {
  let fallTimetable = {
    MONDAY: [],
    TUESDAY: [],
    WEDNESDAY: [],
    THURSDAY: [],
    FRIDAY: [],
  };
  let winterTimetable = {
    MONDAY: [],
    TUESDAY: [],
    WEDNESDAY: [],
    THURSDAY: [],
    FRIDAY: [],
  };

  /**
     * How the following recursive work: Take a (lecture/practical/tutorial) section from a course add to a list,
       take another section from next course add to a list, repeat until the last course.
       Add the sections to the current timetable out of the list and check for validity(if there is any conflict between sections)
       If there is a conflict:
            Revert the timetable to the previous state(the timetable without adding any new sections)
            and continue to for loop the remaining section of the courses
       If there is no conflict:  
            return true
        After lecture section, when there is a no conflict of the courses' lecture, it will move on to start appending practical sections
         if there are any course that has practical, or else move to tutorial section if there are any, or else return true
        After practical section, if there are any course with tutorial, it will move to tutorial after check for validity
    */
  /** lectureCombo.founded are used to terminate "some" function when it continues to loop because of recursion not functioning properly 
     but a valid timetable is already found
     */
  const fallLectureCombo = (courseSection, whichArray, output = []) => {
    fallLectureCombo.founded = 0;
    const lec2 = searchForSectionIndexAfterprevIndex(
      courseSection,
      'lecture',
      whichArray,
    );
    if (lec2 !== -1) {
      return courseSection[whichArray].lecture.some(arrayElement => {
        // Recursive case...
        // if the course is not the last one
        if (fallLectureCombo.founded === 1) {
          return true;
        }
        const temp = [...output];
        temp.push(arrayElement);
        fallLectureCombo(courseSection, lec2, temp);
      });
    } else {
      // Base case...
      return courseSection[whichArray].lecture.some(arrayElement => {
        const temp = [...output];
        temp.push(arrayElement);
        const tempLecList = temp;
        addSectionToTimetable(temp, fallTimetable);
        // if its invalid, clear the timetable and start again
        if (overlapExists(fallTimetable)) {
          fallTimetable = {
            MONDAY: [],
            TUESDAY: [],
            WEDNESDAY: [],
            THURSDAY: [],
            FRIDAY: [],
          };
        } else {
          // check if any course in the combo contains practical
          const pra = searchForSectionIndex(courseSection, 'practical');
          if (pra >= 0) {
            const prevTimetable = createShallowCopyOfTimetable(fallTimetable);
            const practicalCombo = (
              // eslint-disable-next-line no-shadow
              courseSection,
              whichArray2 = pra,
              output2 = [],
            ) => {
              const pra2 = searchForSectionIndexAfterprevIndex(
                courseSection,
                'practical',
                whichArray2,
              );
              if (pra2 !== -1) {
                return courseSection[whichArray2].practical.some(
                  arrayElement2 => {
                    // Recursive case...
                    if (fallLectureCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    practicalCombo(courseSection, pra2, temp);
                  },
                );
              } else {
                return courseSection[whichArray2].practical.some(
                  arrayElement2 => {
                    // Base case when reach until the last course that has practical
                    if (fallLectureCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    const tempPraList = temp;
                    addSectionToTimetable(temp, fallTimetable);
                    if (overlapExists(fallTimetable)) {
                      fallTimetable = createShallowCopyOfTimetable(
                        prevTimetable,
                      );
                      let j = -1;
                      for (let i = 0; i < temp.length; i += 1) {
                        if (
                          temp[i] ===
                          courseSection[i].practical[
                            courseSection[i].practical.length - 1
                          ]
                        ) {
                          j += 1;
                        }
                      }
                      if (j === temp.length - 1) {
                        return false;
                      }
                    } else {
                      const tut = searchForSectionIndex(
                        courseSection,
                        'tutorial',
                      );
                      if (tut >= 0) {
                        const prevTimetabletut = createShallowCopyOfTimetable(
                          fallTimetable,
                        );
                        const tutorialCombo = (
                          // eslint-disable-next-line no-shadow
                          courseSection,
                          // eslint-disable-next-line no-shadow
                          whichArray2 = tut,
                          // eslint-disable-next-line no-shadow
                          output2 = [],
                        ) => {
                          const tut2 = searchForSectionIndexAfterprevIndex(
                            courseSection,
                            'tutorial',
                            whichArray2,
                          );
                          if (tut2 !== -1) {
                            return courseSection[whichArray2].tutorial.some(
                              // eslint-disable-next-line no-shadow
                              arrayElement2 => {
                                // Recursive case...
                                if (fallLectureCombo.founded === 1) {
                                  return true;
                                }
                                // eslint-disable-next-line no-shadow
                                const temp = [...output2];
                                temp.push(arrayElement2);
                                tutorialCombo(courseSection, tut2, temp);
                              },
                            );
                          } else {
                            return courseSection[whichArray2].tutorial.some(
                              // eslint-disable-next-line no-shadow
                              arrayElement2 => {
                                // Base case when reach until the last course that has tutorial
                                if (fallLectureCombo.founded === 1) {
                                  return true;
                                }
                                // eslint-disable-next-line no-shadow
                                const temp = [...output2];
                                temp.push(arrayElement2);
                                addSectionToTimetable(temp, fallTimetable);
                                if (overlapExists(fallTimetable)) {
                                  fallTimetable = createShallowCopyOfTimetable(
                                    prevTimetabletut,
                                  );
                                  let j = -1;
                                  for (let i = 0; i < temp.length; i += 1) {
                                    if (
                                      temp[i] ===
                                      courseSection[i].tutorial[
                                        courseSection[i].tutorial.length - 1
                                      ]
                                    ) {
                                      j += 1;
                                    }
                                  }
                                  if (j === temp.length - 1) {
                                    return false;
                                  }
                                } else {
                                  const yearLocked = [];
                                  const tempList = [...output2];
                                  tempList.push(arrayElement2);
                                  tempList.push(...tempLecList);
                                  tempList.push(...tempPraList);
                                  for (const section of tempList) {
                                    if (
                                      section.comboCode.charAt(
                                        section.comboCode.length - 6,
                                      ) === 'Y'
                                    ) {
                                      yearLocked.push(section.comboCode);
                                    }
                                  }
                                  // eslint-disable-next-line no-shadow
                                  const temp = createCopyOfCourseSection(
                                    winterCourseSection,
                                  );
                                  lockSectionOfCourse(temp, yearLocked);
                                  [, winterTimetable] = createTimetable(
                                    fallCourseSection,
                                    temp,
                                    'W',
                                  );
                                  if (
                                    JSON.stringify(winterTimetable) ===
                                      JSON.stringify({
                                        MONDAY: [],
                                        TUESDAY: [],
                                        WEDNESDAY: [],
                                        THURSDAY: [],
                                        FRIDAY: [],
                                      }) &&
                                    winterCourseSection.length > 0
                                  ) {
                                    fallTimetable = createShallowCopyOfTimetable(
                                      prevTimetabletut,
                                    );
                                  } else {
                                    fallLectureCombo.founded = 1;
                                    // founds a valid timetable
                                    return true;
                                  }
                                }
                              },
                            );
                          }
                        };
                        const tutResult = tutorialCombo(courseSection);
                        if (tutResult) {
                          return true;
                        } else {
                          if (fallLectureCombo.founded === 1) {
                            return true;
                          }
                          fallTimetable = prevTimetable;
                        }
                        if (fallLectureCombo.founded === 1) {
                          return true;
                        }
                      } else {
                        const yearLocked = [];
                        const tempList = [...output2];
                        tempList.push(arrayElement2);
                        tempList.push(...tempLecList);
                        for (const section of tempList) {
                          if (
                            section.comboCode.charAt(
                              section.comboCode.length - 6,
                            ) === 'Y'
                          ) {
                            yearLocked.push(section.comboCode);
                          }
                        }
                        // eslint-disable-next-line no-shadow
                        const temp = createCopyOfCourseSection(
                          winterCourseSection,
                        );
                        lockSectionOfCourse(temp, yearLocked);
                        [, winterTimetable] = createTimetable(
                          fallCourseSection,
                          temp,
                          'W',
                        );
                        if (
                          JSON.stringify(winterTimetable) ===
                            JSON.stringify({
                              MONDAY: [],
                              TUESDAY: [],
                              WEDNESDAY: [],
                              THURSDAY: [],
                              FRIDAY: [],
                            }) &&
                          winterCourseSection.length > 0
                        ) {
                          fallTimetable = createShallowCopyOfTimetable(
                            prevTimetable,
                          );
                        } else {
                          fallLectureCombo.founded = 1;
                          // founds a valid timetable
                          return true;
                        }
                      }
                    }
                  },
                );
              }
            };
            const praResult = practicalCombo(courseSection);
            if (praResult) {
              fallLectureCombo.founded = 1;
              // founds a valid timetable
              return true;
            } else {
              if (fallLectureCombo.founded === 1) {
                return true;
              }
              fallTimetable = {
                MONDAY: [],
                TUESDAY: [],
                WEDNESDAY: [],
                THURSDAY: [],
                FRIDAY: [],
              };
            }
          } else {
            const tut = searchForSectionIndex(courseSection, 'tutorial');
            if (tut >= 0) {
              const prevTimetable = createShallowCopyOfTimetable(fallTimetable);
              const tutorialCombo = (
                // eslint-disable-next-line no-shadow
                courseSection,
                whichArray2 = tut,
                output2 = [],
              ) => {
                const tut2 = searchForSectionIndexAfterprevIndex(
                  courseSection,
                  'tutorial',
                  whichArray2,
                );
                if (tut2 !== -1) {
                  return courseSection[whichArray2].tutorial.some(
                    arrayElement2 => {
                      // Recursive case...
                      if (fallLectureCombo.founded === 1) {
                        return true;
                      }
                      // eslint-disable-next-line no-shadow
                      const temp = [...output2];
                      temp.push(arrayElement2);
                      tutorialCombo(courseSection, tut2, temp);
                    },
                  );
                } else {
                  return courseSection[whichArray2].tutorial.some(
                    arrayElement2 => {
                      // Base case when reach until the last course that has tutorial
                      if (fallLectureCombo.founded === 1) {
                        return true;
                      }
                      // eslint-disable-next-line no-shadow
                      const temp = [...output2];
                      temp.push(arrayElement2);
                      addSectionToTimetable(temp, fallTimetable);
                      if (overlapExists(fallTimetable)) {
                        fallTimetable = createShallowCopyOfTimetable(
                          prevTimetable,
                        );
                        let j = -1;
                        for (let i = 0; i < temp.length; i += 1) {
                          if (
                            temp[i] ===
                            courseSection[i].tutorial[
                              courseSection[i].tutorial.length - 1
                            ]
                          ) {
                            j += 1;
                          }
                        }
                        if (j === temp.length - 1) {
                          return false;
                        }
                      } else {
                        const yearLocked = [];
                        const tempList = [...output2];
                        tempList.push(arrayElement2);
                        tempList.push(...tempLecList);
                        for (const section of tempList) {
                          if (
                            section.comboCode.charAt(
                              section.comboCode.length - 6,
                            ) === 'Y'
                          ) {
                            yearLocked.push(section.comboCode);
                          }
                        }
                        // eslint-disable-next-line no-shadow
                        const temp = createCopyOfCourseSection(
                          winterCourseSection,
                        );
                        lockSectionOfCourse(temp, yearLocked);
                        [, winterTimetable] = createTimetable(
                          fallCourseSection,
                          temp,
                          'W',
                        );
                        if (
                          JSON.stringify(winterTimetable) ===
                            JSON.stringify({
                              MONDAY: [],
                              TUESDAY: [],
                              WEDNESDAY: [],
                              THURSDAY: [],
                              FRIDAY: [],
                            }) &&
                          winterCourseSection.length > 0
                        ) {
                          fallTimetable = createShallowCopyOfTimetable(
                            prevTimetable,
                          );
                        } else {
                          fallLectureCombo.founded = 1;
                          // founds a valid timetable
                          return true;
                        }
                      }
                    },
                  );
                }
              };
              const tutResult = tutorialCombo(courseSection);
              if (tutResult) {
                fallLectureCombo.founded = 1;
                // founds a valid timetable
                return true;
              } else {
                if (fallLectureCombo.founded === 1) {
                  return true;
                }
                fallTimetable = {
                  MONDAY: [],
                  TUESDAY: [],
                  WEDNESDAY: [],
                  THURSDAY: [],
                  FRIDAY: [],
                };
              }
            } else {
              const yearLocked = [];
              const tempList = [...output];
              tempList.push(arrayElement);
              for (const section of tempList) {
                if (
                  section.comboCode.charAt(section.comboCode.length - 6) === 'Y'
                ) {
                  yearLocked.push(section.comboCode);
                }
              }
              // eslint-disable-next-line no-shadow
              const temp = createCopyOfCourseSection(winterCourseSection);
              lockSectionOfCourse(temp, yearLocked);
              [, winterTimetable] = createTimetable(
                fallCourseSection,
                temp,
                'W',
              );
              if (
                JSON.stringify(winterTimetable) ===
                  JSON.stringify({
                    MONDAY: [],
                    TUESDAY: [],
                    WEDNESDAY: [],
                    THURSDAY: [],
                    FRIDAY: [],
                  }) &&
                winterCourseSection.length > 0
              ) {
                fallTimetable = {
                  MONDAY: [],
                  TUESDAY: [],
                  WEDNESDAY: [],
                  THURSDAY: [],
                  FRIDAY: [],
                };
              } else {
                fallLectureCombo.founded = 1;
                // founds a valid timetable
                return true;
              }
            }
            if (fallLectureCombo.founded === 1) {
              return true;
            }
          }
        }
        if (fallLectureCombo.founded === 1) {
          return true;
        }
      });
    }
  };
  // if the course selected dont have any lectures and have practicals and tutorials
  const fallPracticalCombo = (courseSection, whichArray2, output2 = []) => {
    fallPracticalCombo.founded = 0;
    const pra2 = searchForSectionIndexAfterprevIndex(
      courseSection,
      'practical',
      whichArray2,
    );
    if (pra2 !== -1) {
      return courseSection[whichArray2].practical.some(arrayElement2 => {
        // Recursive case...
        if (fallPracticalCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        fallPracticalCombo(courseSection, pra2, temp);
      });
    } else {
      return courseSection[whichArray2].practical.some(arrayElement2 => {
        // Base case when reach until the last course that has practical
        if (fallPracticalCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        const tempPraList = temp;
        addSectionToTimetable(temp, fallTimetable);
        if (overlapExists(fallTimetable)) {
          fallTimetable = {
            MONDAY: [],
            TUESDAY: [],
            WEDNESDAY: [],
            THURSDAY: [],
            FRIDAY: [],
          };
          let j = -1;
          for (let i = 0; i < temp.length; i += 1) {
            if (
              temp[i] ===
              courseSection[i].practical[courseSection[i].practical.length - 1]
            ) {
              j += 1;
            }
          }
          if (j === temp.length - 1) {
            return false;
          }
        } else {
          const tut = searchForSectionIndex(courseSection, 'tutorial');
          if (tut >= 0) {
            const prevTimetabletut = createShallowCopyOfTimetable(
              fallTimetable,
            );
            const tutorialCombo = (
              // eslint-disable-next-line no-shadow
              courseSection,
              // eslint-disable-next-line no-shadow
              whichArray2 = tut,
              // eslint-disable-next-line no-shadow
              output2 = [],
            ) => {
              const tut2 = searchForSectionIndexAfterprevIndex(
                courseSection,
                'tutorial',
                whichArray2,
              );
              if (tut2 !== -1) {
                return courseSection[whichArray2].tutorial.some(
                  // eslint-disable-next-line no-shadow
                  arrayElement2 => {
                    // Recursive case...
                    if (fallPracticalCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    tutorialCombo(courseSection, tut2, temp);
                  },
                );
              } else {
                return courseSection[whichArray2].tutorial.some(
                  // eslint-disable-next-line no-shadow
                  arrayElement2 => {
                    // Base case when reach until the last course that has tutorial
                    if (fallPracticalCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    addSectionToTimetable(temp, fallTimetable);
                    if (overlapExists(fallTimetable)) {
                      fallTimetable = createShallowCopyOfTimetable(
                        prevTimetabletut,
                      );
                      let j = -1;
                      for (let i = 0; i < temp.length; i += 1) {
                        if (
                          temp[i] ===
                          courseSection[i].tutorial[
                            courseSection[i].tutorial.length - 1
                          ]
                        ) {
                          j += 1;
                        }
                      }
                      if (j === temp.length - 1) {
                        return false;
                      }
                    } else {
                      const yearLocked = [];
                      const tempList = [...output2];
                      tempList.push(arrayElement2);
                      tempList.push(...tempPraList);
                      for (const section of tempList) {
                        if (
                          section.comboCode.charAt(
                            section.comboCode.length - 6,
                          ) === 'Y'
                        ) {
                          yearLocked.push(section.comboCode);
                        }
                      }
                      // eslint-disable-next-line no-shadow
                      const temp = createCopyOfCourseSection(
                        winterCourseSection,
                      );
                      lockSectionOfCourse(temp, yearLocked);
                      [, winterTimetable] = createTimetable(
                        fallCourseSection,
                        temp,
                        'W',
                      );
                      if (
                        JSON.stringify(winterTimetable) ===
                          JSON.stringify({
                            MONDAY: [],
                            TUESDAY: [],
                            WEDNESDAY: [],
                            THURSDAY: [],
                            FRIDAY: [],
                          }) &&
                        winterCourseSection.length > 0
                      ) {
                        fallTimetable = createShallowCopyOfTimetable(
                          prevTimetabletut,
                        );
                      } else {
                        fallPracticalCombo.founded = 1;
                        // founds a valid timetable
                        return true;
                      }
                    }
                  },
                );
              }
            };
            const tutResult = tutorialCombo(courseSection);
            if (tutResult) {
              return true;
            } else {
              if (fallPracticalCombo.founded === 1) {
                return true;
              }
              fallTimetable = {
                MONDAY: [],
                TUESDAY: [],
                WEDNESDAY: [],
                THURSDAY: [],
                FRIDAY: [],
              };
            }
            if (fallPracticalCombo.founded === 1) {
              return true;
            }
          } else {
            const yearLocked = [];
            const tempList = [...output2];
            tempList.push(arrayElement2);
            for (const section of tempList) {
              if (
                section.comboCode.charAt(section.comboCode.length - 6) === 'Y'
              ) {
                yearLocked.push(section.comboCode);
              }
            }
            // eslint-disable-next-line no-shadow
            const temp = createCopyOfCourseSection(winterCourseSection);
            lockSectionOfCourse(temp, yearLocked);
            [, winterTimetable] = createTimetable(fallCourseSection, temp, 'W');
            if (
              JSON.stringify(winterTimetable) ===
                JSON.stringify({
                  MONDAY: [],
                  TUESDAY: [],
                  WEDNESDAY: [],
                  THURSDAY: [],
                  FRIDAY: [],
                }) &&
              winterCourseSection.length > 0
            ) {
              fallTimetable = {
                MONDAY: [],
                TUESDAY: [],
                WEDNESDAY: [],
                THURSDAY: [],
                FRIDAY: [],
              };
            } else {
              fallPracticalCombo.founded = 1;
              // founds a valid timetable
              return true;
            }
          }
        }
      });
    }
  };
  // if the course selected dont have any lectures nor practicals and have only tutorials

  const fallTutorialCombo = (courseSection, whichArray2, output2 = []) => {
    fallTutorialCombo.founded = 0;
    const tut2 = searchForSectionIndexAfterprevIndex(
      courseSection,
      'tutorial',
      whichArray2,
    );
    if (tut2 !== -1) {
      return courseSection[whichArray2].tutorial.some(arrayElement2 => {
        // Recursive case...
        if (fallTutorialCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        fallTutorialCombo(courseSection, tut2, temp);
      });
    } else {
      return courseSection[whichArray2].tutorial.some(arrayElement2 => {
        // Base case when reach until the last course that has tutorial
        if (fallTutorialCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        addSectionToTimetable(temp, fallTimetable);
        if (overlapExists(fallTimetable)) {
          fallTimetable = {
            MONDAY: [],
            TUESDAY: [],
            WEDNESDAY: [],
            THURSDAY: [],
            FRIDAY: [],
          };
          let j = -1;
          for (let i = 0; i < temp.length; i += 1) {
            if (
              temp[i] ===
              courseSection[i].tutorial[courseSection[i].tutorial.length - 1]
            ) {
              j += 1;
            }
          }
          if (j === temp.length - 1) {
            return false;
          }
        } else {
          const yearLocked = [];
          const tempList = [...output2];
          tempList.push(arrayElement2);
          for (const section of tempList) {
            if (
              section.comboCode.charAt(section.comboCode.length - 6) === 'Y'
            ) {
              yearLocked.push(section.comboCode);
            }
          }
          // eslint-disable-next-line no-shadow
          const temp = createCopyOfCourseSection(winterCourseSection);
          lockSectionOfCourse(temp, yearLocked);
          [, winterTimetable] = createTimetable(fallCourseSection, temp, 'W');
          if (
            JSON.stringify(winterTimetable) ===
              JSON.stringify({
                MONDAY: [],
                TUESDAY: [],
                WEDNESDAY: [],
                THURSDAY: [],
                FRIDAY: [],
              }) &&
            winterCourseSection.length > 0
          ) {
            fallTimetable = {
              MONDAY: [],
              TUESDAY: [],
              WEDNESDAY: [],
              THURSDAY: [],
              FRIDAY: [],
            };
          } else {
            fallTutorialCombo.founded = 1;
            // founds a valid timetable
            return true;
          }
        }
      });
    }
  };
  const winterLectureCombo = (courseSection, whichArray, output = []) => {
    winterLectureCombo.founded = 0;
    const lec2 = searchForSectionIndexAfterprevIndex(
      courseSection,
      'lecture',
      whichArray,
    );
    if (lec2 !== -1) {
      return courseSection[whichArray].lecture.some(arrayElement => {
        // Recursive case...
        // if the course is not the last one
        if (winterLectureCombo.founded === 1) {
          return true;
        }
        const temp = [...output];
        temp.push(arrayElement);
        winterLectureCombo(courseSection, lec2, temp);
      });
    } else {
      return courseSection[whichArray].lecture.some(arrayElement => {
        if (winterLectureCombo.founded === 1) {
          return true;
        }
        // Base case: If the course is the last one

        // Base case...
        const temp = [...output];
        temp.push(arrayElement);
        addSectionToTimetable(temp, winterTimetable);
        // if its invalid, clear the timetable and start again
        if (overlapExists(winterTimetable)) {
          winterTimetable = {
            MONDAY: [],
            TUESDAY: [],
            WEDNESDAY: [],
            THURSDAY: [],
            FRIDAY: [],
          };
        } else {
          // check if any course in the combo contains practical
          const pra = searchForSectionIndex(courseSection, 'practical');
          if (pra >= 0) {
            const prevTimetable = createShallowCopyOfTimetable(winterTimetable);
            const practicalCombo = (
              // eslint-disable-next-line no-shadow
              courseSection,
              whichArray2 = pra,
              output2 = [],
            ) => {
              const pra2 = searchForSectionIndexAfterprevIndex(
                courseSection,
                'practical',
                whichArray2,
              );
              if (pra2 !== -1) {
                return courseSection[whichArray2].practical.some(
                  arrayElement2 => {
                    // Recursive case...
                    if (winterLectureCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    practicalCombo(courseSection, pra2, temp);
                  },
                );
              } else {
                return courseSection[whichArray2].practical.some(
                  arrayElement2 => {
                    // Base case when reach until the last course that has practical
                    if (winterLectureCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    addSectionToTimetable(temp, winterTimetable);
                    if (overlapExists(winterTimetable)) {
                      winterTimetable = createShallowCopyOfTimetable(
                        prevTimetable,
                      );
                      let j = -1;
                      for (let i = 0; i < temp.length; i += 1) {
                        if (
                          temp[i] ===
                          courseSection[i].practical[
                            courseSection[i].practical.length - 1
                          ]
                        ) {
                          j += 1;
                        }
                      }
                      if (j === temp.length - 1) {
                        return false;
                      }
                    } else {
                      const tut = searchForSectionIndex(
                        courseSection,
                        'tutorial',
                      );
                      if (tut >= 0) {
                        const prevTimetabletut = createShallowCopyOfTimetable(
                          winterTimetable,
                        );
                        const tutorialCombo = (
                          // eslint-disable-next-line no-shadow
                          courseSection,
                          // eslint-disable-next-line no-shadow
                          whichArray2 = tut,
                          // eslint-disable-next-line no-shadow
                          output2 = [],
                        ) => {
                          const tut2 = searchForSectionIndexAfterprevIndex(
                            courseSection,
                            'tutorial',
                            whichArray2,
                          );
                          if (tut2 !== -1) {
                            return courseSection[whichArray2].tutorial.some(
                              // eslint-disable-next-line no-shadow
                              arrayElement2 => {
                                // Recursive case...
                                if (winterLectureCombo.founded === 1) {
                                  return true;
                                }
                                // eslint-disable-next-line no-shadow
                                const temp = [...output2];
                                temp.push(arrayElement2);
                                tutorialCombo(courseSection, tut2, temp);
                              },
                            );
                          } else {
                            return courseSection[whichArray2].tutorial.some(
                              // eslint-disable-next-line no-shadow
                              arrayElement2 => {
                                // Base case when reach until the last course that has tutorial
                                if (winterLectureCombo.founded === 1) {
                                  return true;
                                }
                                // eslint-disable-next-line no-shadow
                                const temp = [...output2];
                                temp.push(arrayElement2);
                                addSectionToTimetable(temp, winterTimetable);
                                if (overlapExists(winterTimetable)) {
                                  winterTimetable = createShallowCopyOfTimetable(
                                    prevTimetabletut,
                                  );
                                  let j = -1;
                                  for (let i = 0; i < temp.length; i += 1) {
                                    if (
                                      temp[i] ===
                                      courseSection[i].tutorial[
                                        courseSection[i].tutorial.length - 1
                                      ]
                                    ) {
                                      j += 1;
                                    }
                                  }
                                  if (j === temp.length - 1) {
                                    return false;
                                  }
                                } else {
                                  winterLectureCombo.founded = 1;
                                  // founds a valid timetable
                                  return true;
                                }
                              },
                            );
                          }
                        };
                        const tutResult = tutorialCombo(courseSection);
                        if (tutResult) {
                          return true;
                        } else {
                          if (winterLectureCombo.founded === 1) {
                            return true;
                          }
                          winterTimetable = prevTimetable;
                        }
                        if (winterLectureCombo.founded === 1) {
                          return true;
                        }
                      } else {
                        winterLectureCombo.founded = 1;
                        return true;
                      }
                    }
                  },
                );
              }
            };
            const praResult = practicalCombo(courseSection);
            if (praResult) {
              winterLectureCombo.founded = 1;
              // founds a valid timetable
              return true;
            } else {
              if (winterLectureCombo.founded === 1) {
                return true;
              }
              winterTimetable = {
                MONDAY: [],
                TUESDAY: [],
                WEDNESDAY: [],
                THURSDAY: [],
                FRIDAY: [],
              };
            }
          } else {
            const tut = searchForSectionIndex(courseSection, 'tutorial');
            if (tut >= 0) {
              const prevTimetable = createShallowCopyOfTimetable(
                winterTimetable,
              );
              const tutorialCombo = (
                // eslint-disable-next-line no-shadow
                courseSection,
                whichArray2 = tut,
                output2 = [],
              ) => {
                const tut2 = searchForSectionIndexAfterprevIndex(
                  courseSection,
                  'tutorial',
                  whichArray2,
                );
                if (tut2 !== -1) {
                  return courseSection[whichArray2].tutorial.some(
                    arrayElement2 => {
                      // Recursive case...
                      if (fallLectureCombo.founded === 1) {
                        return true;
                      }
                      // eslint-disable-next-line no-shadow
                      const temp = [...output2];
                      temp.push(arrayElement2);
                      tutorialCombo(courseSection, tut2, temp);
                    },
                  );
                } else {
                  return courseSection[whichArray2].tutorial.some(
                    arrayElement2 => {
                      // Base case when reach until the last course that has tutorial
                      if (winterLectureCombo.founded === 1) {
                        return true;
                      }
                      // eslint-disable-next-line no-shadow
                      const temp = [...output2];
                      temp.push(arrayElement2);
                      addSectionToTimetable(temp, winterTimetable);
                      if (overlapExists(winterTimetable)) {
                        winterTimetable = createShallowCopyOfTimetable(
                          prevTimetable,
                        );
                        let j = -1;
                        for (let i = 0; i < temp.length; i += 1) {
                          if (
                            temp[i] ===
                            courseSection[i].tutorial[
                              courseSection[i].tutorial.length - 1
                            ]
                          ) {
                            j += 1;
                          }
                        }
                        if (j === temp.length - 1) {
                          return false;
                        }
                      } else {
                        winterLectureCombo.founded = 1;
                        return true;
                      }
                    },
                  );
                }
              };
              const tutResult = tutorialCombo(courseSection);
              if (tutResult) {
                winterLectureCombo.founded = 1;
                // founds a valid timetable
                return true;
              } else {
                if (winterLectureCombo.founded === 1) {
                  return true;
                }
                winterTimetable = {
                  MONDAY: [],
                  TUESDAY: [],
                  WEDNESDAY: [],
                  THURSDAY: [],
                  FRIDAY: [],
                };
              }
            } else {
              winterLectureCombo.founded = 1;
              // founds a valid timetable
              return true;
            }
            if (winterLectureCombo.founded === 1) {
              return true;
            }
          }
        }
        if (winterLectureCombo.founded === 1) {
          return true;
        }
      });
    }
  };
  const winterPracticalCombo = (courseSection, whichArray2, output2 = []) => {
    winterPracticalCombo.founded = 0;
    const pra2 = searchForSectionIndexAfterprevIndex(
      courseSection,
      'practical',
      whichArray2,
    );
    if (pra2 !== -1) {
      return courseSection[whichArray2].practical.some(arrayElement2 => {
        // Recursive case...
        if (winterPracticalCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        winterPracticalCombo(courseSection, pra2, temp);
      });
    } else {
      return courseSection[whichArray2].practical.some(arrayElement2 => {
        // Base case when reach until the last course that has practical
        if (winterPracticalCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        addSectionToTimetable(temp, winterTimetable);
        if (overlapExists(winterTimetable)) {
          winterTimetable = {
            MONDAY: [],
            TUESDAY: [],
            WEDNESDAY: [],
            THURSDAY: [],
            FRIDAY: [],
          };
          let j = -1;
          for (let i = 0; i < temp.length; i += 1) {
            if (
              temp[i] ===
              courseSection[i].practical[courseSection[i].practical.length - 1]
            ) {
              j += 1;
            }
          }
          if (j === temp.length - 1) {
            return false;
          }
        } else {
          const tut = searchForSectionIndex(courseSection, 'tutorial');
          if (tut >= 0) {
            const prevTimetabletut = createShallowCopyOfTimetable(
              winterTimetable,
            );
            const tutorialCombo = (
              // eslint-disable-next-line no-shadow
              courseSection,
              // eslint-disable-next-line no-shadow
              whichArray2 = tut,
              // eslint-disable-next-line no-shadow
              output2 = [],
            ) => {
              const tut2 = searchForSectionIndexAfterprevIndex(
                courseSection,
                'tutorial',
                whichArray2,
              );
              if (tut2 !== -1) {
                return courseSection[whichArray2].tutorial.some(
                  // eslint-disable-next-line no-shadow
                  arrayElement2 => {
                    // Recursive case...
                    if (winterPracticalCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    tutorialCombo(courseSection, tut2, temp);
                  },
                );
              } else {
                return courseSection[whichArray2].tutorial.some(
                  // eslint-disable-next-line no-shadow
                  arrayElement2 => {
                    // Base case when reach until the last course that has tutorial
                    if (winterPracticalCombo.founded === 1) {
                      return true;
                    }
                    // eslint-disable-next-line no-shadow
                    const temp = [...output2];
                    temp.push(arrayElement2);
                    addSectionToTimetable(temp, winterTimetable);
                    if (overlapExists(winterTimetable)) {
                      winterTimetable = createShallowCopyOfTimetable(
                        prevTimetabletut,
                      );
                      let j = -1;
                      for (let i = 0; i < temp.length; i += 1) {
                        if (
                          temp[i] ===
                          courseSection[i].tutorial[
                            courseSection[i].tutorial.length - 1
                          ]
                        ) {
                          j += 1;
                        }
                      }
                      if (j === temp.length - 1) {
                        return false;
                      }
                    } else {
                      winterPracticalCombo.founded = 1;
                      // founds a valid timetable
                      return true;
                    }
                  },
                );
              }
            };
            const tutResult = tutorialCombo(courseSection);
            if (tutResult) {
              return true;
            } else {
              if (winterPracticalCombo.founded === 1) {
                return true;
              }
              winterTimetable = {
                MONDAY: [],
                TUESDAY: [],
                WEDNESDAY: [],
                THURSDAY: [],
                FRIDAY: [],
              };
            }
            if (winterPracticalCombo.founded === 1) {
              return true;
            }
          } else {
            winterPracticalCombo.founded = 1;
            return true;
          }
        }
      });
    }
  };
  const winterTutorialCombo = (courseSection, whichArray2, output2 = []) => {
    winterTutorialCombo.founded = 0;
    const tut2 = searchForSectionIndexAfterprevIndex(
      courseSection,
      'tutorial',
      whichArray2,
    );
    if (tut2 !== -1) {
      return courseSection[whichArray2].tutorial.some(arrayElement2 => {
        // Recursive case...
        if (winterTutorialCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        winterTutorialCombo(courseSection, tut2, temp);
      });
    } else {
      return courseSection[whichArray2].tutorial.some(arrayElement2 => {
        // Base case when reach until the last course that has tutorial
        if (winterTutorialCombo.founded === 1) {
          return true;
        }
        const temp = [...output2];
        temp.push(arrayElement2);
        addSectionToTimetable(temp, winterTimetable);
        if (overlapExists(winterTimetable)) {
          winterTimetable = {
            MONDAY: [],
            TUESDAY: [],
            WEDNESDAY: [],
            THURSDAY: [],
            FRIDAY: [],
          };
          let j = -1;
          for (let i = 0; i < temp.length; i += 1) {
            if (
              temp[i] ===
              courseSection[i].tutorial[courseSection[i].tutorial.length - 1]
            ) {
              j += 1;
            }
          }
          if (j === temp.length - 1) {
            return false;
          }
        } else {
          winterTutorialCombo.founded = 1;
          return true;
        }
      });
    }
  };

  if (fallCourseSection.length > 0 && state === 'F') {
    const lec = searchForSectionIndex(fallCourseSection, 'lecture');
    const pra = searchForSectionIndex(fallCourseSection, 'practical');
    const tut = searchForSectionIndex(fallCourseSection, 'tutorial');
    if (lec >= 0) {
      fallLectureCombo(fallCourseSection, lec);
    } else if (pra >= 0) {
      fallPracticalCombo(fallCourseSection, pra);
    } else if (tut >= 0) {
      fallTutorialCombo(fallCourseSection, tut);
    }
  } else if (fallCourseSection.length === 0 && state === 'F') {
    [, winterTimetable] = createTimetable(
      fallCourseSection,
      winterCourseSection,
      'W',
    );
  }
  if (winterCourseSection.length > 0 && state === 'W') {
    const lec = searchForSectionIndex(winterCourseSection, 'lecture');
    const pra = searchForSectionIndex(winterCourseSection, 'practical');
    const tut = searchForSectionIndex(winterCourseSection, 'tutorial');
    if (lec >= 0) {
      winterLectureCombo(winterCourseSection, lec);
    } else if (pra >= 0) {
      winterPracticalCombo(winterCourseSection, pra);
    } else if (tut >= 0) {
      winterTutorialCombo(winterCourseSection, tut);
    }
  }
  const days = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY'];
  if (state === 'F') {
    for (const day of days) {
      fallTimetable[day].sort((a, b) => a.start - b.start);
      winterTimetable[day].sort((a, b) => a.start - b.start);
    }
  }
  return [fallTimetable, winterTimetable];
};
/**
 *
 * The main function.
 * Starts from produce all section combinations of each course
 * Produce the combinations of the courses' section combinations
 * Create Timetable for each combinations of section combinations
 * Returns the master list of Timetables
 * @param {Course[]} courses
 * @returns {Timetable[]}
 */
const generateTimetables = (
  fallCourses,
  fallLockSections,
  winterCourses,
  winterLockSections,
  online,
  excludedConflictCourses = [],
) => {
  // Generate all valid combinations of MeetingSections for a course
  console.log(excludedConflictCourses);
  const fallCourseSections = fallCourses.map(course =>
    sortCourseSection(course),
  );
  const winterCourseSections = winterCourses.map(course =>
    sortCourseSection(course),
  );
  lockSectionOfCourse(fallCourseSections, fallLockSections);
  lockSectionOfCourse(winterCourseSections, winterLockSections);
  sortCourses(fallCourseSections, online);
  sortCourses(winterCourseSections, online);
  
  let timetables = createTimetable(
    fallCourseSections,
    winterCourseSections,
    'F',
  );
  if (
    (JSON.stringify(timetables[0]) ===
      JSON.stringify({
        MONDAY: [],
        TUESDAY: [],
        WEDNESDAY: [],
        THURSDAY: [],
        FRIDAY: [],
      }) &&
      fallCourses.length > 0) ||
    (JSON.stringify(timetables[1]) ===
      JSON.stringify({
        MONDAY: [],
        TUESDAY: [],
        WEDNESDAY: [],
        THURSDAY: [],
        FRIDAY: [],
      }) &&
      winterCourses.length > 0)
  ) {
    timetables = null;
  }
  return timetables;
};
// export { generateTimetables, createTimetable, overlapExists };
module.exports = { generateTimetables, createTimetable, overlapExists };
// # sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGtCQUFrQixFQUFFLGdDQUFnQyxHQUFHLE1BQU0sNkJBQTZCLENBQUE7QUFFbkc7Ozs7OztHQU1HO0FBQ0gsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLFNBQW9CLEVBQUUsR0FBVyxFQUFFLEVBQUU7SUFDN0QsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFBO0lBQ2YsT0FBTyxPQUFPLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRTtRQUNwQyxJQUFJLFFBQVEsR0FBRyxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUMsQ0FBQTtRQUM1QixPQUFPLFFBQVEsR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFO1lBQ3JDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsS0FBSyxJQUFJLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLO2dCQUNoRSxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxHQUFHLENBQUM7Z0JBQzdELENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsS0FBSztvQkFDekQsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsSUFBSSxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUU7Z0JBQ2xFLE9BQU8sSUFBSSxDQUFBO2FBQ2Q7WUFDRCxRQUFRLEVBQUUsQ0FBQTtTQUNiO1FBQ0QsT0FBTyxFQUFFLENBQUE7S0FDWjtJQUNELE9BQU8sS0FBSyxDQUFBO0FBQ2hCLENBQUMsQ0FBQTtBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxhQUFhLEdBQUcsQ0FBQyxTQUFvQixFQUFXLEVBQUU7SUFDcEQsTUFBTSxJQUFJLEdBQUcsQ0FBQyxRQUFRLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUE7SUFDckUsSUFBSSxNQUFNLEdBQUcsS0FBSyxDQUFBO0lBQ2xCLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxFQUFFO1FBQ3BCLE1BQU0sR0FBRyxNQUFNLElBQUksa0JBQWtCLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFBO0tBQ3hEO0lBQ0QsT0FBTyxNQUFNLENBQUE7QUFDakIsQ0FBQyxDQUFBO0FBR0Q7Ozs7O0dBS0c7QUFDSCxNQUFNLGVBQWUsR0FBRyxDQUFDLG1CQUFxQyxFQUFhLEVBQUU7SUFDekUsTUFBTSxTQUFTLEdBQWM7UUFDekIsTUFBTSxFQUFFLEVBQUU7UUFDVixPQUFPLEVBQUUsRUFBRTtRQUNYLFNBQVMsRUFBRSxFQUFFO1FBQ2IsUUFBUSxFQUFFLEVBQUU7UUFDWixNQUFNLEVBQUUsRUFBRTtLQUNiLENBQUE7SUFDRCxLQUFLLE1BQU0sY0FBYyxJQUFJLG1CQUFtQixFQUFFO1FBQzlDLEtBQUssTUFBTSxJQUFJLElBQUksY0FBYyxDQUFDLEtBQUssRUFBRTtZQUNyQyxNQUFNLGdCQUFnQixHQUFxQjtnQkFDdkMsSUFBSSxFQUFFLGNBQWMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBQyxFQUFFLENBQUM7Z0JBQ3pDLFdBQVcsRUFBRSxjQUFjLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7Z0JBQzlDLFdBQVcsRUFBRSxjQUFjLENBQUMsV0FBVztnQkFDdkMsR0FBRyxJQUFJO2FBQ1YsQ0FBQTtZQUNELFNBQVMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUE7U0FDN0M7S0FDSjtJQUNELElBQUksYUFBYSxDQUFDLFNBQVMsQ0FBQyxFQUFFO1FBQzFCLE9BQU8sSUFBSSxDQUFBO0tBQ2Q7SUFDRCxNQUFNLElBQUksR0FBRyxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUNyRSxLQUFLLE1BQU0sR0FBRyxJQUFJLElBQUksRUFBRTtRQUNwQixTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ3pCLE9BQU8sQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFBO1FBQzVCLENBQUMsQ0FBQyxDQUFBO0tBQ0w7SUFDRCxPQUFPLFNBQVMsQ0FBQTtBQUNwQixDQUFDLENBQUE7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLGtCQUFrQixHQUFHLENBQUMsT0FBaUIsRUFBZSxFQUFFO0lBRTFELGtFQUFrRTtJQUNsRSxNQUFNLDBCQUEwQixHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxnQ0FBZ0MsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO0lBRWxHLE1BQU0sbUJBQW1CLEdBQUcsa0JBQWtCLENBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUMzRSxNQUFNLFVBQVUsR0FBZ0IsRUFBRSxDQUFBO0lBRWxDLEtBQUssTUFBTSxZQUFZLElBQUksbUJBQW1CLEVBQUU7UUFDNUMsTUFBTSxTQUFTLEdBQUcsZUFBZSxDQUFDLFlBQVksQ0FBQyxDQUFBO1FBQy9DLElBQUksU0FBUyxJQUFJLElBQUksRUFBRTtZQUNuQixVQUFVLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFBO1NBQzdCO0tBQ0o7SUFFRCxPQUFPLFVBQVUsQ0FBQTtBQUNyQixDQUFDLENBQUE7QUFDRCxPQUFPLEVBQ0gsa0JBQWtCLEVBQ2xCLGVBQWUsRUFDZixhQUFhLEVBQ2hCLENBQUEifQ==