/// <summary> /// To force a split having the most populated class. /// If ParameterMostPopulatedClassInEverySplitsValue = 1 /// else it will be skipped /// </summary> /// <param name="s"></param> private bool AddMostPopulatedClassInTheSplitIfMissing(List <Split> splits, Split s) { bool ret = false; // if we want the most populated class on each split if (true /*ParameterMostPopulatedClassInEverySplits*/) { // add most populated class in the split : // first, the the list not classes not in this split List <int> classesNotInThisSplit = new List <int>(); for (int i = 0; i < carClassesIds.Count - 2; i++) { if (s.CountClassCars(i) == 0) { classesNotInThisSplit.Add(carClassesIds[i]); } } // get the most populated class id we want to have int mostPopClassId = carClassesIds.Last(); // create a move dictionnary // KEY : populated class id // VALUES : missing cars to fit the good number of cars in this populated class Dictionary <int, int> mostPopAdd = new Dictionary <int, int>(); int mostPopTarget = TakeCars(carClassesIds, splits, s, mostPopClassId, null, fieldSize); mostPopTarget -= s.CountClassCars(carClassesIds.Count - 1); // --> // there is a move up to do if (mostPopTarget > 0) { // so do it mostPopAdd.Add(mostPopClassId, mostPopTarget); UpCarsToSplit(carClassesIds, splits, s, mostPopAdd); ret = true; // --> } } return(ret); }
/// <summary> /// This method generate a split from combination (Truth Tabl) /// It will convert fill corresponding classes described in a combination (with true/false) /// with concrete cars list, taking the right number /// </summary> /// <param name="splitNumber"></param> /// <param name="c"></param> /// <param name="previousSplit"></param> /// <returns></returns> private Split GenerateSplitFromCombination(int splitNumber, Calc.Combination c, Split previousSplit = null) { // create a new split Split s = new Split(splitNumber); // how may classes in this combination int splitClasses = c.ClassesId.Length; // foreach enabled classes var enabledClassesId = c.EnabledClassesId; for (int i = 0; i < splitClasses; i++) { if (c.Enabled[i]) { // get class info int classId = c.ClassesId[i]; int classIndex = classesIds.IndexOf(classId); // call the affineDistributor to calc the right number of cars int totake = affineDistributor.TakeCars(classId, enabledClassesId, fieldSize); int toskip = 0; if (previousSplit != null) { // if this split is the second, and have a previous split // we need to skip cars already in that previous split toskip = previousSplit.CountClassCars(classIndex); } // get cars var cars = classesQueues[classIndex].GetFirstCars(toskip, totake); // put them in the split s.SetClass(classIndex, cars, classId); } } return(s); }
/// <summary> /// If there is too much cars in the split (more than fieldSize) /// Than this method will move down the excess /// </summary> /// <param name="s"></param> /// <returns></returns> private List <int> MoveDownExcessCarsInTheSplit(List <Split> splits, Split s) { List <int> classesToReducted = new List <int>(); // list classes in this split and class not in this splits List <int> classesInTheSplit = new List <int>(); List <int> classesNotInTheSplit = new List <int>(); for (int i = 0; i < carClassesIds.Count; i++) { if (s.CountClassCars(i) > 0) { classesInTheSplit.Add(carClassesIds[i]); } else { classesNotInTheSplit.Add(carClassesIds[i]); } } // --> // get limits list by querying nominal cars count for a // complete field size with same car classes // KEY = classid // VALUE = targetted cars number Dictionary <int, int> classLimits = new Dictionary <int, int>(); foreach (var c in classesInTheSplit) { int limit = TakeCars(splits, s, c, classesNotInTheSplit, fieldSize); classLimits.Add(c, limit); } // --> // for each limit foreach (var limit in classLimits) { // get class and max cars count wanted int classId = limit.Key; int classIndex = carClassesIds.IndexOf(classId); int max = limit.Value; // count if to much cars int carsToMove = s.CountClassCars(classIndex) - max; // yes there are cars in excess for that class // while there is for (int i = 0; i < carsToMove; i++) { // get the next split containng the same class var nextSplitContainingSameClassCars = (from r in splits where r.Number > s.Number && r.CountClassCars(classIndex) > 0 select r).FirstOrDefault(); // it the parameter to force most populated class in every // split is set to yes, than simply get the next split // nevermind if it contains the same class or not we will add it if (true /*ParameterMostPopulatedClassInEverySplits*/) { if (classIndex == carClassesIds.Count - 1) { // so get the next split nextSplitContainingSameClassCars = (from r in splits where r.Number > s.Number select r).FirstOrDefault(); if (nextSplitContainingSameClassCars != null) { // if this split does not contains the class yet, create it if (nextSplitContainingSameClassCars.GetClassId(classIndex) != classId) { nextSplitContainingSameClassCars.SetClass(classIndex, classId); } // --> } } } // --> // if the next split to put cars on is not found if (nextSplitContainingSameClassCars == null) { // just get the next one neverless contains the class yet nextSplitContainingSameClassCars = (from r in splits where r.Number > s.Number select r).FirstOrDefault(); if (nextSplitContainingSameClassCars != null) { // if this split does not contains the class yet, create it if (nextSplitContainingSameClassCars.GetClassId(classIndex) != classId) { nextSplitContainingSameClassCars.SetClass(classIndex, classId); } // --> } } // is the target split is here ? if (nextSplitContainingSameClassCars != null) { // pick one car to the current split 's' (in excess) var pick = s.PickClassCars(classIndex, 1, true); // and append them to the targeted one nextSplitContainingSameClassCars.AppendClassCars(classIndex, pick); } else { // no... hm // no move possible // keeps car in it for the moment } // add to the return on that method the classId we reduced if (!classesToReducted.Contains(classId)) { classesToReducted.Add(classId); } } // end ot the for loop 'carsToMove' } // end of the foreach loop class limit // return the list of classesId reduced return(classesToReducted); }
/// <summary> /// Move down cars from a split to the lower one /// </summary> /// <param name="s"></param> /// <param name="splitIndex"></param> public void MoveDownCarsSplits(List <Split> splits, Split s, int splitIndex, int?forceMoveDownOfClassIndex = null) { List <int> classesSof = CalcSplitSofs(s); // classes we move down in this split List <int> movedCategories = new List <int>(); // --> // ensure most populated category is filled it option set bool mostPopCatAsBeenFilled = AddMostPopulatedClassInTheSplitIfMissing(splits, s); // --> // move cars down int classesToMoveDown = classesSof.Count - 1; if (false /*ParameterMostPopulatedClassInEverySplits*/) { // if the parameter MostPopulatedClassInEverySplits is disabled // then we will also check the last class classesToMoveDown++; } for (int i = classesToMoveDown - 1; i >= 0; i--) //for (int i = 0; i < classesToMoveDown; i++) { // test: do we need to move down ? bool doTheMoveDown = false; if (forceMoveDownOfClassIndex != null) { doTheMoveDown = (i == forceMoveDownOfClassIndex.Value); } else { doTheMoveDown = HaveToMoveDown(s, i, classesSof); } if (forceMoveDownOfClassIndex == null && INTERRUPT_BEFORE_MOVEDOWN_SPLITNUMBER == s.Number && INTERRUPT_BEFORE_MOVEDOWN_CLASSINDEX == i) { // to help debugging return; } // if we have to move the class to the next split if (doTheMoveDown) { // pick all the class cars var cars = s.PickClassCars(i); s.SetClassTarget(i, 0); // add them to the next split Split nextSplit = GetSplit(splits, splitIndex + 1); AppendCarsToSplit(nextSplit, i, cars); if (cars.Count > 0) { movedCategories.Add(carClassesIds[i]); } } } // --> // reset next split ? //if (s.Number + 1 < splits.Count) //{ // var nextSplit2 = splits[s.Number + 1]; // ResetSplitWithAllClassesFilled(splits, nextSplit2); //} // --> // up cars to fill leaved slots List <int> doNotUpCategories = new List <int>(); doNotUpCategories.AddRange(movedCategories); if (mostPopCatAsBeenFilled) { int mostPopupClassId = carClassesIds[carClassesIds.Count - 1]; if (!doNotUpCategories.Contains(mostPopupClassId)) { doNotUpCategories.Add(mostPopupClassId); } } UpCarsToSplit(splits, s, doNotUpCategories); // --> // to much cars in the split, move them down // reducedClasses is the ids of the classes recudes // keep then in a variable because we don't want // to update them again after that var reducedClasses = MoveDownExcessCarsInTheSplit(splits, s); for (int e = s.Number; e < splits.Count - 1; e++) { MoveDownExcessCarsInTheSplit(splits, splits[e]); } // --> // build the exception list to classes to lock on this split // movedCategories = union of [movedCategories + reducedClasses + empty classes of 0 cars] foreach (var c in reducedClasses) { if (!movedCategories.Contains(c)) { movedCategories.Add(c); } } foreach (var c in carClassesIds) { int classIndex = carClassesIds.IndexOf(c); if (s.CountClassCars(classIndex) == 0) { if (!movedCategories.Contains(c)) { movedCategories.Add(c); } } } // --> // up cars to fill leaved slot again // for classe not in the exception list 'movedCategories' UpCarsToSplit(splits, s, movedCategories); // --> }