attestation-to-examiner-distribution
Version:
An API to calculate the precedence diagram method
481 lines (480 loc) • 27.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const JSONToGraph_1 = __importDefault(require("./JSONToGraph"));
const js_graph_algorithms_1 = __importDefault(require("js-graph-algorithms"));
class GraphHelper {
static getJSGraphsFlowNetwork(graphAndVerticeMaps) {
let graphRaw = graphAndVerticeMaps.graph;
let fromIds = Object.keys(graphRaw);
let amountOfVertices = fromIds.length;
//console.log("amountOfVertices: "+amountOfVertices);
let g = new js_graph_algorithms_1.default.FlowNetwork(amountOfVertices);
let amountEdges = 0;
for (const fromId of fromIds) {
//console.log("fromId: "+fromId);
let toIds = Object.keys(graphRaw[fromId]);
for (const toId of toIds) {
//console.log("toId: "+toId);
let edgeInformation = graphRaw[fromId][toId];
let capacity = edgeInformation.capacity;
let flow = edgeInformation.flow;
let from = edgeInformation.from;
let to = edgeInformation.to;
//@ts-ignore
g.addEdge(new js_graph_algorithms_1.default.FlowEdge(fromId, toId, capacity));
amountEdges++;
}
}
g.node(0).label = 'Source';
g.node(1).label = 'Sink';
return g;
}
/**
* Returns the maximum flow of the given graph by reducing the tutor capacity until the maximum flow is still reached.
* @param unoptimizedJSON
*/
static getTutorCapacityWhereAllHaveSameMaximum(unoptimizedJSON) {
console.log("============================================");
console.log("getTutorCapacityWhereAllHaveSameMaximum");
let amountGroups = Object.keys(unoptimizedJSON.groups).length;
let initialTutorCapacity = amountGroups;
let graphAndVerticeMaps = JSONToGraph_1.default.getGraphAndVerticeMaps(unoptimizedJSON, initialTutorCapacity);
let source = graphAndVerticeMaps.source;
let sink = graphAndVerticeMaps.sink;
let g = GraphHelper.getJSGraphsFlowNetwork(graphAndVerticeMaps);
let maxFlow = GraphHelper.getMaxFlow(g, source, sink);
// binary search for the minimum tutor capacity
let lowestTutorCapacity = initialTutorCapacity;
let nextTutorCapacity = Math.floor(initialTutorCapacity / 2);
while (lowestTutorCapacity != nextTutorCapacity) {
let lastNextTutorCapacity = nextTutorCapacity;
let newgraphAndVerticeMaps = JSONToGraph_1.default.getGraphAndVerticeMaps(unoptimizedJSON, nextTutorCapacity);
let newG = GraphHelper.getJSGraphsFlowNetwork(newgraphAndVerticeMaps);
let newMaxFlow = GraphHelper.getMaxFlow(newG, source, sink);
if (newMaxFlow >= maxFlow) {
// newMaxFlow == maxFlow so we can lower the tutor capacity
lowestTutorCapacity = nextTutorCapacity;
nextTutorCapacity = Math.floor(nextTutorCapacity / 2);
}
else {
// newMaxFlow != maxFlow so we have to increase the tutor capacity
nextTutorCapacity = Math.floor((lowestTutorCapacity + nextTutorCapacity) / 2);
if (lastNextTutorCapacity == nextTutorCapacity) {
// lastNextTutorCapacity == nextTutorCapacity so we can't lower the tutor capacity anymore
nextTutorCapacity = lowestTutorCapacity;
break;
}
}
}
return nextTutorCapacity;
}
static getTutorOptimizationWhereAllHaveSameMinimum(unoptimizedJSON, maxTutorCapacity) {
console.log("Okay we have a optimized plan where we know that the maximum");
console.log("tutor capacity is " + maxTutorCapacity);
let graphAndVerticeMaps = JSONToGraph_1.default.getGraphAndVerticeMaps(unoptimizedJSON, maxTutorCapacity);
let g = GraphHelper.getJSGraphsFlowNetwork(graphAndVerticeMaps);
let source = graphAndVerticeMaps.source;
let sink = graphAndVerticeMaps.sink;
let maxFlow = GraphHelper.getMaxFlow(g, source, sink);
console.log("maxFlow: " + maxFlow);
let currentMaxFlow = maxFlow;
// so every tutor has the same upper bound, now we have to find the minimum tutor capacity, because it might be
// that some tutors have a lower capacity and we want to shuffle them to the tutors with the higher capacity
// Step 1: Get the tutors current individual capacities
let currentDictTutorToIndividualDiff = {};
let previousTutorDistribution = GraphHelper.getDictTutorToAmountGroups(unoptimizedJSON);
console.log("previousTutorDistribution: ");
console.log(previousTutorDistribution);
let currentOptimizedJSON = JSON.parse(JSON.stringify(unoptimizedJSON));
let keepOptimizing = true;
let iteration = 0;
let maxIterations = 1000;
while (keepOptimizing && iteration < maxIterations) {
iteration++;
console.log("---- new iteration ----");
let nextDictTutorToIndividualDiff = GraphHelper.getDictTutorToIndividualDiff(currentOptimizedJSON, maxTutorCapacity, currentDictTutorToIndividualDiff);
console.log("nextDictTutorToIndividualDiff: ");
console.log(JSON.stringify(nextDictTutorToIndividualDiff));
graphAndVerticeMaps = JSONToGraph_1.default.getGraphAndVerticeMaps(currentOptimizedJSON, maxTutorCapacity, nextDictTutorToIndividualDiff);
g = GraphHelper.getJSGraphsFlowNetwork(graphAndVerticeMaps);
source = graphAndVerticeMaps.source;
sink = graphAndVerticeMaps.sink;
currentMaxFlow = GraphHelper.getMaxFlow(g, source, sink);
let nextOptimizedJSON = GraphHelper.getTutorDistributionFromGraphAndVerticeMaps(currentOptimizedJSON, graphAndVerticeMaps);
// we dont need to add the diff to the tutor capacities, because we already did that in the previous step
//let optimizedTutorDistribution = GraphHelper.getDictTutorToAmountGroups(optimizedJSONWhereAllTutorsHaveTheSameMaximum);
//console.log("optimizedTutorDistribution: ");
//console.log(optimizedTutorDistribution);
let sameFlow = currentMaxFlow == maxFlow;
let differentDistribution = JSON.stringify(currentDictTutorToIndividualDiff) != JSON.stringify(nextDictTutorToIndividualDiff);
// when we have the same max flow and the distribution is different, we keep the new distribution
if (sameFlow && differentDistribution) {
currentDictTutorToIndividualDiff = nextDictTutorToIndividualDiff;
currentOptimizedJSON = JSON.parse(JSON.stringify(nextOptimizedJSON));
keepOptimizing = true;
}
else {
console.log("Optimization finished!");
console.log("sameFlow: " + sameFlow);
console.log("differentDistribution: " + differentDistribution);
keepOptimizing = false;
}
}
console.log("=============================================");
console.log("dictTutorToIndividualDiff: ");
console.log(currentDictTutorToIndividualDiff);
let finalTutorDistribution = GraphHelper.getDictTutorToAmountGroups(currentOptimizedJSON);
console.log("finalTutorDistribution: ");
console.log(finalTutorDistribution);
return currentOptimizedJSON;
}
static getDictTutorToIndividualDiff(unoptimizedJSON, maxTutorCapacity, dictTutorToIndividualDiff) {
let copyDictTutorToIndividualDiff = JSON.parse(JSON.stringify(dictTutorToIndividualDiff));
let tutorDistributionInformation = GraphHelper.getDictTutorDistributionInformation(unoptimizedJSON, maxTutorCapacity);
let tutorHighestPercentageValue = undefined;
let tutorHighestPercentageName = undefined;
let tutorLowestPercentageValue = undefined;
let tutorLowestPercentageName = undefined;
let tutorKeys = Object.keys(tutorDistributionInformation);
for (let tutorKey of tutorKeys) {
// @ts-ignore
let tutorPercentage = tutorDistributionInformation[tutorKey].percentage;
if (tutorHighestPercentageValue == undefined || tutorPercentage > tutorHighestPercentageValue) {
tutorHighestPercentageValue = tutorPercentage;
tutorHighestPercentageName = tutorKey;
}
if (tutorLowestPercentageValue == undefined || tutorPercentage < tutorLowestPercentageValue) {
tutorLowestPercentageValue = tutorPercentage;
tutorLowestPercentageName = tutorKey;
}
}
console.log("- tutorHighestPercentageName: " + tutorHighestPercentageName);
console.log("- tutorHighestPercentageValue: " + tutorHighestPercentageValue);
console.log("- tutorLowestPercentageName: " + tutorLowestPercentageName);
console.log("- tutorLowestPercentageValue: " + tutorLowestPercentageValue);
if (tutorHighestPercentageName && tutorLowestPercentageName && tutorHighestPercentageName != tutorLowestPercentageName) {
let tutorHighestPercentageCurrentDiff = copyDictTutorToIndividualDiff[tutorHighestPercentageName] || 0;
let tutorLowestPercentageCurrentDiff = copyDictTutorToIndividualDiff[tutorLowestPercentageName] || 0;
if (tutorLowestPercentageCurrentDiff < 0 || tutorHighestPercentageCurrentDiff > 0) {
// if the person with the lowest percentage has a negative diff already, we dont want to increase it
// this would cause a loop
// if the person with the highest percentage has a positive diff already, we dont want to decrease it
// this would cause a loop
return copyDictTutorToIndividualDiff; // we return the old dict
}
else {
copyDictTutorToIndividualDiff[tutorHighestPercentageName] = tutorHighestPercentageCurrentDiff - 1;
copyDictTutorToIndividualDiff[tutorLowestPercentageName] = tutorLowestPercentageCurrentDiff + 1;
}
}
return copyDictTutorToIndividualDiff;
}
static getDictTutorDistributionInformation(unoptimizedJSON, maxTutorCapacity) {
var _a;
let tutorDistribution = GraphHelper.getDictTutorToAmountGroups(unoptimizedJSON);
let dictTutorDistributionInformation = {};
for (let tutor in tutorDistribution) {
let amountGroups = tutorDistribution[tutor];
let multiplier = ((_a = unoptimizedJSON === null || unoptimizedJSON === void 0 ? void 0 : unoptimizedJSON.tutorMultipliers) === null || _a === void 0 ? void 0 : _a[tutor]) || 1;
let aimedMaxCapacity = maxTutorCapacity * multiplier;
// @ts-ignore
dictTutorDistributionInformation[tutor] = {
amountGroups: amountGroups,
aimedMaxCapacity: aimedMaxCapacity,
percentage: amountGroups / aimedMaxCapacity
};
}
return dictTutorDistributionInformation;
}
static getGraphFromGraphRaw(graphRaw, nameToVertice, verticeToName, tutorCapacity) {
//@ts-ignore
let graph = JSONToGraph_1.default.getGraph(graphRaw, nameToVertice, tutorCapacity);
//@ts-ignore
let g = GraphHelper.getJSGraphsFlowNetwork(graph, nameToVertice, verticeToName);
return g;
}
static getMinCut(graphAndVerticeMaps) {
let g = GraphHelper.getJSGraphsFlowNetwork(graphAndVerticeMaps);
let ff = new js_graph_algorithms_1.default.FordFulkerson(g, graphAndVerticeMaps.source, graphAndVerticeMaps.sink);
return ff.minCut(g);
}
static getMaxFlow(g, source, sink) {
let ff = new js_graph_algorithms_1.default.FordFulkerson(g, source, sink);
return ff.value;
}
static getTutorDistributionFromGraphAndVerticeMaps(unoptimizedJSON, graphAndVerticeMaps) {
let minCut = GraphHelper.getMinCut(graphAndVerticeMaps);
for (let i = 0; i < minCut.length; i++) {
let e = minCut[i];
//@ts-ignore
graphAndVerticeMaps.graph[e.from()][e.to()].flow = e.flow;
}
let optimizedJSON = JSON.parse(JSON.stringify(unoptimizedJSON));
let groupNames = Object.keys(optimizedJSON.groups);
for (const groupName of groupNames) {
let groupVertice = graphAndVerticeMaps.nameToVertice[groupName];
let slotNodesForGroup = graphAndVerticeMaps.graph[groupVertice];
let slotNodeIdsForGroup = Object.keys(slotNodesForGroup);
for (const slotNodeId of slotNodeIdsForGroup) {
let slotNode = slotNodesForGroup[slotNodeId];
let flow = slotNode.flow;
if (flow > 0) {
let slotName = graphAndVerticeMaps.verticeToName[slotNodeId];
let slotWithName = graphAndVerticeMaps.slotsWithNames[slotName];
optimizedJSON.groups[groupName]["selectedSlot"] = slotWithName;
}
}
}
return optimizedJSON;
}
static getTutorDistribution(unoptimizedJSON, tutorCapacity) {
let graphAndVerticeMaps = JSONToGraph_1.default.getGraphAndVerticeMaps(unoptimizedJSON, tutorCapacity);
let optimizedJSON = GraphHelper.getTutorDistributionFromGraphAndVerticeMaps(unoptimizedJSON, graphAndVerticeMaps);
return optimizedJSON;
}
static getDictTutorToAmountGroups(unoptimizedJSON, dictTutorToIndividualDiff) {
let tutors = unoptimizedJSON.tutors;
let tutorNames = Object.keys(tutors);
let dictTutorToCapacity = {};
for (const tutor of tutorNames) {
// @ts-ignore
dictTutorToCapacity[tutor] = 0;
}
let groups = unoptimizedJSON.groups;
let groupKeys = Object.keys(groups);
for (const groupKey of groupKeys) {
let group = groups[groupKey];
let selectedSlot = group.selectedSlot;
let tutor = selectedSlot.tutor;
// @ts-ignore
if (dictTutorToCapacity[tutor] == undefined) {
// @ts-ignore
dictTutorToCapacity[tutor] = 1;
}
else {
// @ts-ignore
dictTutorToCapacity[tutor] += 1;
}
}
if (!!dictTutorToIndividualDiff) {
let tutorKeysFromDictTutorToIndividualDiff = Object.keys(dictTutorToIndividualDiff);
for (const tutorKey of tutorKeysFromDictTutorToIndividualDiff) {
// @ts-ignore
dictTutorToCapacity[tutorKey] += dictTutorToIndividualDiff[tutorKey];
}
}
return dictTutorToCapacity;
}
static removeUnnecessarySwitchesInSameSlots(oldPlan, unoptimizedJSON) {
var _a, _b;
console.log("=====================================");
console.log("removeUnnecessarySwitchesInSameSlots");
let newPlan = JSON.parse(JSON.stringify(unoptimizedJSON));
let timeslots = JSONToGraph_1.default.getTimeslots();
for (let i = 0; i < timeslots.length; i++) {
let time = timeslots[i];
let workingWeekdays = JSONToGraph_1.default.getWorkingWeekdays();
for (let day of workingWeekdays) {
let groupsAtTimeAndDayWithDifferentTutor = [];
let oldGroups = oldPlan.groups;
//@ts-ignore
let newGroups = newPlan === null || newPlan === void 0 ? void 0 : newPlan.groups;
let oldGroupNames = Object.keys(oldGroups);
for (let i = 0; i < oldGroupNames.length; i++) {
let groupName = oldGroupNames[i];
let oldSelectedSlot = (_a = oldGroups === null || oldGroups === void 0 ? void 0 : oldGroups[groupName]) === null || _a === void 0 ? void 0 : _a.selectedSlot;
let newSelectedSlot = (_b = newGroups === null || newGroups === void 0 ? void 0 : newGroups[groupName]) === null || _b === void 0 ? void 0 : _b.selectedSlot;
let oldSelectedTutor = oldSelectedSlot === null || oldSelectedSlot === void 0 ? void 0 : oldSelectedSlot.tutor;
let newSelectedTutor = newSelectedSlot === null || newSelectedSlot === void 0 ? void 0 : newSelectedSlot.tutor;
let useSelectedSlot = !!newSelectedSlot ? newSelectedSlot : oldSelectedSlot;
if (useSelectedSlot.day === day && useSelectedSlot.time === time && oldSelectedTutor !== newSelectedTutor) {
groupsAtTimeAndDayWithDifferentTutor.push(groupName);
}
}
if (groupsAtTimeAndDayWithDifferentTutor.length >= 2) {
newPlan = this.removeUnnecessarySwitchesInSameSlot(oldPlan, newPlan, groupsAtTimeAndDayWithDifferentTutor, day, time);
}
}
}
return newPlan;
}
static removeUnnecessarySwitchesInSameSlot(oldPlan, newPlan, groupsAtTimeAndDay, day, time) {
// console.log("====================================");
console.log("removeUnnecessarySwitchesInSameSlot for day: " + day + " and time: " + time);
let initialAmountOfSwitches = this.countAmountOfSwitchesInPlan(oldPlan, newPlan, groupsAtTimeAndDay);
// console.log("initialAmountOfSwitches: " + initialAmountOfSwitches);
// console.log("groupsAtTimeAndDay: ");
// console.log(groupsAtTimeAndDay);
let tutorsArray = this.getTutorsArrayForGroupsAtTimeAndDays(newPlan, groupsAtTimeAndDay);
let tutorPermutations = this.getTutorPermutations(tutorsArray);
let newPlanCopy = JSON.parse(JSON.stringify(newPlan));
let bestPlan = newPlanCopy;
let bestAmountOfSwitches = initialAmountOfSwitches;
for (let i = 0; i < tutorPermutations.length; i++) {
let tutorPermutation = tutorPermutations[i];
let newPlanWithSwitchedTutors = this.getPlanWithSwitchedTutors(newPlanCopy, tutorPermutation, groupsAtTimeAndDay);
let amountOfSwitches = this.countAmountOfSwitchesInPlan(oldPlan, newPlanWithSwitchedTutors, groupsAtTimeAndDay);
// console.log("Permutation "+i+" amountOfSwitches: " + amountOfSwitches);
if (amountOfSwitches < bestAmountOfSwitches) {
bestAmountOfSwitches = amountOfSwitches;
bestPlan = newPlanWithSwitchedTutors;
}
}
console.log("Saved " + (initialAmountOfSwitches - bestAmountOfSwitches) + " switches");
// we need to check if there is any permutation of the which has less switches
return bestPlan;
}
static getPlanWithSwitchedTutors(newPlan, tutorPermutationArray, groupsAtTimeAndDayArray) {
let newPlanCopy = JSON.parse(JSON.stringify(newPlan));
for (let i = 0; i < groupsAtTimeAndDayArray.length; i++) {
let groupName = groupsAtTimeAndDayArray[i];
let tutorName = tutorPermutationArray[i];
let group = newPlanCopy.groups[groupName];
group.selectedSlot.tutor = tutorName;
newPlanCopy.groups[groupName] = group;
}
return newPlanCopy;
}
/**
* Return all permutations of the tutorsArray
* input: ["apple", "banana", "kiwi"]
*
* [["apple", "banana", "kiwi"]
* ["apple", "kiwi", "banana"]
* ["kiwi", "banana", "apple"]
* ["kiwi", "apple", "banana"]
* ["banana", "kiwi", "apple"]
* ["banana", "apple", "kiwi"]]
*
* @param tutorsArray
* @returns all permutations of the tutorsArray
*/
static getTutorPermutations(tutorsArray) {
let arr = tutorsArray;
const output = [];
const n = arr.length;
function swap(i, j) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function generate(n, arr) {
if (n === 1) {
output.push([...arr]);
return;
}
for (let i = 0; i < n; i++) {
generate(n - 1, arr);
if (n % 2 === 0) {
swap(i, n - 1);
}
else {
swap(0, n - 1);
}
}
}
generate(n, arr);
return output;
}
static getTutorsArrayForGroupsAtTimeAndDays(newPlan, groupsAtTimeAndDay) {
var _a, _b;
let tutorsArray = [];
for (let i = 0; i < groupsAtTimeAndDay.length; i++) {
let groupName = groupsAtTimeAndDay[i];
let selectedSlot = (_b = (_a = newPlan.groups) === null || _a === void 0 ? void 0 : _a[groupName]) === null || _b === void 0 ? void 0 : _b.selectedSlot;
let tutor = selectedSlot === null || selectedSlot === void 0 ? void 0 : selectedSlot.tutor;
tutorsArray.push(tutor);
}
return tutorsArray;
}
static countAmountOfSwitchesInPlan(oldPlan, newPlan, groupsAtTimeAndDay) {
var _a, _b, _c, _d;
let amountOfSwitches = 0;
for (let i = 0; i < groupsAtTimeAndDay.length; i++) {
let groupName = groupsAtTimeAndDay[i];
let oldSelectedSlot = (_b = (_a = oldPlan.groups) === null || _a === void 0 ? void 0 : _a[groupName]) === null || _b === void 0 ? void 0 : _b.selectedSlot;
let newSelectedSlot = (_d = (_c = newPlan.groups) === null || _c === void 0 ? void 0 : _c[groupName]) === null || _d === void 0 ? void 0 : _d.selectedSlot;
let oldSelectedTutor = oldSelectedSlot === null || oldSelectedSlot === void 0 ? void 0 : oldSelectedSlot.tutor;
let newSelectedTutor = newSelectedSlot === null || newSelectedSlot === void 0 ? void 0 : newSelectedSlot.tutor;
if (oldSelectedTutor !== newSelectedTutor) {
amountOfSwitches++;
}
}
return amountOfSwitches;
}
static mergeSingleGroups(unoptimizedJSON) {
var _a;
console.log("=====================================");
console.log("mergeSingleGroups");
let newPlan = JSON.parse(JSON.stringify(unoptimizedJSON));
let timeslots = JSONToGraph_1.default.getTimeslots();
for (let i = 0; i < timeslots.length; i++) {
let time = timeslots[i];
let workingWeekdays = JSONToGraph_1.default.getWorkingWeekdays();
for (let day of workingWeekdays) {
let oldGroups = unoptimizedJSON.groups;
let oldGroupNames = Object.keys(oldGroups);
let singleGroups = [];
for (let i = 0; i < oldGroupNames.length; i++) {
let currentGroupName = oldGroupNames[i];
let useSelectedSlot = (_a = oldGroups === null || oldGroups === void 0 ? void 0 : oldGroups[currentGroupName]) === null || _a === void 0 ? void 0 : _a.selectedSlot;
if (useSelectedSlot.day === day && useSelectedSlot.time === time) {
let currentGroup = oldGroups === null || oldGroups === void 0 ? void 0 : oldGroups[currentGroupName];
let members = currentGroup.members;
if (members.length === 1) {
console.log("Found single group: " + currentGroupName + " at " + day + " " + time);
singleGroups.push(currentGroupName);
if (singleGroups.length >= 2) {
console.log("Found 2 single groups at the same time and day: " + singleGroups[0] + " and " + singleGroups[1]);
// we have at least 2 single groups at the same time and day, so we can merge them
// lets get the two members
let otherGroupName = singleGroups[0];
let otherGroup = oldGroups === null || oldGroups === void 0 ? void 0 : oldGroups[otherGroupName];
let otherGroupMember = otherGroup.members[0];
let currentGroupMember = currentGroup.members[0];
// lets add the member of the other group to the current group
let groupMembers = [otherGroupMember, currentGroupMember];
let groupId = groupMembers.join(" & ");
// lets create a new group
console.log("Creating new group: " + groupId + " with members: " + groupMembers);
newPlan.groups[groupId] = {};
newPlan.groups[groupId]["members"] = groupMembers;
newPlan.groups[groupId]["selectedSlot"] = JSON.parse(JSON.stringify(currentGroup.selectedSlot));
newPlan.groups[groupId]["possibleSlots"] = {}; // reset the possibleSlots
newPlan.groups[groupId]["possibleSlots"][day] = {};
newPlan.groups[groupId]["possibleSlots"][day][time] = true; // set the possibleSlot
// lets delete the other group
console.log("Deleting group: " + otherGroupName + " and " + currentGroupName);
delete newPlan.groups[otherGroupName];
delete newPlan.groups[currentGroupName];
singleGroups = []; // reset the array
}
}
}
}
}
}
return newPlan;
}
static getOptimizedDistribution(unoptimizedJSON) {
console.log("getOptimizedDistribution");
let oldPlan = JSON.parse(JSON.stringify(unoptimizedJSON));
let newPlan = JSON.parse(JSON.stringify(unoptimizedJSON));
let optionRemoveUnnecessarySwitches = true;
let optionOptimizeMinimum = true;
let maxTutorCapacity = GraphHelper.getTutorCapacityWhereAllHaveSameMaximum(newPlan);
console.log("maxTutorCapacity: " + maxTutorCapacity);
newPlan = GraphHelper.getTutorDistribution(unoptimizedJSON, maxTutorCapacity);
if (optionOptimizeMinimum) {
newPlan = GraphHelper.getTutorOptimizationWhereAllHaveSameMinimum(newPlan, maxTutorCapacity);
}
if (optionRemoveUnnecessarySwitches) {
newPlan = GraphHelper.removeUnnecessarySwitchesInSameSlots(oldPlan, newPlan);
}
return newPlan;
}
}
exports.default = GraphHelper;