Java tutorial
package structuredoutputcbr.adaptation; /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ import structuredoutputcbr.StructuredOutputCBR; import structuredoutputcbr.ontology.*; import structuredoutputcbr.casebase.*; import structuredoutputcbr.utils.*; import java.util.*; import org.apache.commons.collections4.CollectionUtils; /** * CLASS THAT IMPLEMENTS THE ADAPTATION PHASE OF THE CBR * @author Joan T. Matamalas <jtmatamalas@gmail.com> */ public class Adaptation { private Case new_case; // The new case tried to be solved private Case retrieved; // The most similar case in the case base to the one tried to be solved /*------------------------------------------- ONTOLOGY ELEMENT SHORTCUTS -------------------------------------------*/ private final OntologyElement planning_key = StructuredOutputCBR.ontology.getElement("planning"); private final OntologyElement sessions_key = StructuredOutputCBR.ontology.getElement("sessions"); private final OntologyElement program_key = StructuredOutputCBR.ontology.getElement("program"); private final OntologyElement number_key = StructuredOutputCBR.ontology.getElement("number"); private final OntologyElement type_key = StructuredOutputCBR.ontology.getElement("type"); private final OntologyElement muscular_group_key = StructuredOutputCBR.ontology.getElement("muscular group"); private final OntologyElement muscle_key = StructuredOutputCBR.ontology.getElement("muscle"); private final OntologyElement handicaps_key = StructuredOutputCBR.ontology.getElement("handicaps"); private final OntologyElement expertise_key = StructuredOutputCBR.ontology.getElement("expertise"); private final OntologyElement relaxations_key = StructuredOutputCBR.ontology.getElement("relaxations"); private final OntologyElement region_key = StructuredOutputCBR.ontology.getElement("region"); private final OntologyElement exercise_key = StructuredOutputCBR.ontology.getElement("exercise"); private final OntologyElement shedule_key = StructuredOutputCBR.ontology.getElement("schedule"); /** * Principal Funcion. If a new_case description and its must similar case in the case base system it will performs an adaptation over the similar solution in order to adjust to the new construction. * @param new_case The new case description * @param retrieved The most similar case * @return The adapted solution to the new description */ public Case adapt(Case new_case, Case retrieved) { this.new_case = new_case;//The new case this.retrieved = retrieved;//The retrieved case this.new_case.setSolution(new CaseBlock(this.retrieved.getSolution()));//Set the solution of the retrieved case as solution to the new case TO BE ADAPTEd //Get the number sessions of the recieved and new cases int number_of_sessions_retrieved = retrieved.getDescription().getAttributeInteger(this.sessions_key, 0); int number_of_sessions_new = new_case.getDescription().getAttributeInteger(this.sessions_key, 0); //Expertise level of the new case OntologyElement new_level = this.new_case.getDescription().getAttributeOntology(this.expertise_key, 0); //PERFORMS THE ADAPTATION OF THE NUMBER OF SESSIONS ATTIBUTE IF IS IT NECESSARY if (number_of_sessions_new < number_of_sessions_retrieved) { this.adaptHigherNumberOfSessions(number_of_sessions_new, number_of_sessions_retrieved); } else if (number_of_sessions_new > number_of_sessions_retrieved) { this.adaptLowerNumberOfSessions(number_of_sessions_new, number_of_sessions_retrieved); } //PERFORMS THE ADAPTATION OF THE EXPERTISE LEVEL, IF THE ADAPTATION IS REQUIRED IS CONTROLED INSIDE THE FUNCTION this.adaptExpertiseLevel(new_level); //IF THERE ARE RELAXATION, CALLS THE METHOD THAT APPLY THEM. if (!this.new_case.getDescription().getAttributeOntologies(this.relaxations_key).isEmpty()) { this.applyRelaxations(); } //IF THERE ARE HANDICAPS, CALLS THE METHOD THAT APPLY THEM. if (!this.new_case.getDescription().getAttributeOntologies(this.handicaps_key).isEmpty()) { this.applyHandicapConstrains(); } //CHECK THE ORDER OF THE EXERCISES IN THE PLANNING FOLLOWING THE PREFERENCE MATRIX for (int i = 0; i < this.new_case.getSolution().getAttributeComponents(this.planning_key).size(); i++) { Collections.sort((ArrayList<CaseBlock>) this.new_case.getSolution() .getAttributeComponents(this.planning_key).get(i).getAttributeComponents(this.program_key), StructuredOutputCBR.preferences); } //return the new case with the solution adapted return this.new_case; } /** * Adapt the solution if the case retrieved and the case tried to be solved differ in the number of sessions, x_new.sessions is less than x_retrieved.sessions * @param sessions_new Number of sessions in the description of the new case * @param sessions_retrieved Number of sessions in the description of the retrieved case. */ private void adaptHigherNumberOfSessions(Integer sessions_new, Integer sessions_retrieved) { //Get a second similar case with number of sessions as discriminant value OntologyElement k = StructuredOutputCBR.ontology.getElement("sessions"); KeyValuePair<OntologyElement, Integer> pair = new KeyValuePair<>(k, sessions_new); String type = (String) this.retrieved.getDescription().getAttributeOntology(this.type_key, 0).getName(); ArrayList<CaseBlock> new_planning = new ArrayList(); for (int day : this.new_case.getDescription().getAttributeIntegers(this.shedule_key)) { CaseBlock plan = new CaseBlock(); plan.setAttributeContent(this.number_key, day); new_planning.add(plan); } ArrayList<Case> cases_r = StructuredOutputCBR.base.retrieveCasesByDescription(new_case, 1, pair); Case retrieved_2 = cases_r.get(0); this.new_case.setSolution(new CaseBlock(retrieved_2.getSolution())); //Array with all the muscular groups ArrayList<OntologyElement> muscular_groups = this.muscular_group_key.getRelationValues("items"); // Get case exercises ArrayList<CaseBlock> planning_retrieved_1 = this.retrieved.getSolution() .getAttributeComponents(this.planning_key); ArrayList<CaseBlock> planning_retrieved_2 = retrieved_2.getSolution() .getAttributeComponents(this.planning_key); ArrayList<CaseBlock> exercicies_from_retrieved_1 = this.getAllExercisesFromAPlanning(planning_retrieved_1); ArrayList<CaseBlock> exercicies_from_retrieved_2 = this.getAllExercisesFromAPlanning(planning_retrieved_2); ArrayList<CaseBlock> new_exercise_set = new ArrayList(); //Shuffle the muscular groups in order to get random day assignation. DELETE THE COMMMENT!!!!! Collections.shuffle(muscular_groups, new Random(1)); //Iterate over all muscular groups for (OntologyElement group : muscular_groups) { //Get all the exercises for specific muscular group from the retrieved case ArrayList<CaseBlock> muscular_group_exercises_retrieved_1 = this .getAllExercisesFromMuscularGroup(exercicies_from_retrieved_1, group); //Get all the exercises for specific muscular group from the second retrieved case ArrayList<CaseBlock> muscular_group_exercises_retrieved_2 = this .getAllExercisesFromMuscularGroup(exercicies_from_retrieved_2, group); if ((muscular_group_exercises_retrieved_1.size() == muscular_group_exercises_retrieved_2.size()) || muscular_group_exercises_retrieved_2.isEmpty()) continue; //Get the number of exercicies of both sets. int number_of_exercises_retrieved_2 = muscular_group_exercises_retrieved_2.size(); ArrayList<ArrayList<CaseBlock>> combinations = nCr(muscular_group_exercises_retrieved_1, number_of_exercises_retrieved_2); //Index of the best combination int best_combination_size = 0; ArrayList<CaseBlock> best_combination = new ArrayList(); //Get the best combination for (ArrayList<CaseBlock> combination : combinations) { //The muscles for each intervening exercise ArrayList<ArrayList<OntologyElement>> muscles_for_exercises = new ArrayList(); //Get all the muscles of the exercises for (CaseBlock exercise : combination) { OntologyElement ex = exercise.getAttributeOntology(this.exercise_key, 0); muscles_for_exercises.add(ex.getRelationValues("muscles")); } //Start the union of Muscles ArrayList<OntologyElement> unionMuscles = (ArrayList<OntologyElement>) muscles_for_exercises.get(0); //Make the union of all the muscular sets for (int i = 1; i < muscles_for_exercises.size(); i++) { unionMuscles = (ArrayList<OntologyElement>) CollectionUtils.union(unionMuscles, muscles_for_exercises.get(i)); } Collections.sort(unionMuscles, new ComparatorMuscles()); //Update the best if (unionMuscles.size() > best_combination_size) { best_combination_size = unionMuscles.size(); best_combination = combination; } } new_exercise_set = (ArrayList<CaseBlock>) CollectionUtils.union(new_exercise_set, best_combination); } //Fill the planning this.fillPlanning(type, new_exercise_set, new_planning); //Set the new planning as a solution of the new case this.new_case.getSolution().setAttributeContent(this.planning_key, new_planning); } /** * Adapt the solution if the case retrieved and the case tried to be solved differ in the number of sessions, x_new.sessions is more than x_retrieved.sessions * @param sessions_new Number of sessions in the description of the new case * @param sessions_retrieved Number of sessions in the description of the retrieved case. */ private void adaptLowerNumberOfSessions(Integer sessions_new, Integer sessions_retrieved) { //Get a second similar case with number of sessions as discriminant value OntologyElement k = StructuredOutputCBR.ontology.getElement("sessions"); KeyValuePair<OntologyElement, Integer> pair = new KeyValuePair<>(k, sessions_new); String type = (String) this.retrieved.getDescription().getAttributeOntology(this.type_key, 0).getName(); ArrayList<CaseBlock> new_planning = new ArrayList(); for (int day : this.new_case.getDescription().getAttributeIntegers(this.shedule_key)) { CaseBlock plan = new CaseBlock(); plan.setAttributeContent(this.number_key, day); new_planning.add(plan); } ArrayList<Case> cases_r = StructuredOutputCBR.base.retrieveCasesByDescription(new_case, 1, pair); Case retrieved_2 = cases_r.get(0); this.new_case.setSolution(new CaseBlock(retrieved_2.getSolution())); } /** * Adapt the solution if the case retrieved and the case tried to be solved differ the expertise level * @param new_level the level of the new case */ private void adaptExpertiseLevel(OntologyElement new_level) { //The actual planning ArrayList<CaseBlock> planning = this.new_case.getSolution().getAttributeComponents(this.planning_key); //Get the exercises for a given dificulty value ArrayList<OntologyElement> begginerExercises = StructuredOutputCBR.ontology.getElement("beginner") .getRelationValues("exercises"); ArrayList<OntologyElement> intermediateExercises = StructuredOutputCBR.ontology.getElement("intermediate") .getRelationValues("exercises"); //All the used exercises ArrayList<OntologyElement> existingExercises = this.getAllExercisesFromAPlanning_OE(planning); //Adapt the plan for (CaseBlock session : planning) { ArrayList<CaseBlock> program = session.getAttributeComponents(this.program_key); for (CaseBlock ex : program) { //Actual exercise OntologyElement exercise = ex.getAttributeOntology(this.exercise_key, 0); //Level of the exercise OntologyElement exercise_level = exercise.getRelationValue("expertise"); //If the level of the exercise if greater than the desired if ((new_level.getName().equalsIgnoreCase("beginner") && (exercise_level.getName().equalsIgnoreCase("intermediate") || exercise_level.getName().equalsIgnoreCase("advanced"))) || (new_level.getName().equalsIgnoreCase("intermediate") && exercise_level.getName().equalsIgnoreCase("advanced"))) { //Start the comprarator with the actual exercise ExerciseComparatorDisjunction comparatorEx = new ExerciseComparatorDisjunction(exercise); //If the level desired is beginner if (new_level.getName().equalsIgnoreCase("beginner")) { //Sort begginer exercises by the ones that has minimum disjunction Collections.sort(begginerExercises, comparatorEx); boolean substitution_found = false; //Browse all the sorted begginer exercises for (OntologyElement beg_exercise : begginerExercises) { //compute the size of the intersection int size_intersection = CollectionUtils .intersection(exercise.getRelationValues("muscles"), beg_exercise.getRelationValues("muscles")) .size(); //If this exercise exist in the current planning get the next exercise if (existingExercises.contains(beg_exercise)) { continue; } //If the size_intersection consider the intermediate exercises if (size_intersection == 0) { break; } else { //Else substitut the exercise substitution_found = true; ex.setAttributeContent(this.exercise_key, beg_exercise); existingExercises.add(beg_exercise); break; } } if (substitution_found) { continue; } //If(the actual level is advance conside the intermediate exercises if (exercise_level.getName().contains("advanced")) { //Sort the intermediate exercises by disjuntion Collections.sort(intermediateExercises, comparatorEx); //The same than before but with interemediate exercises for (OntologyElement int_exercise : intermediateExercises) { int size_intersection = CollectionUtils .intersection(exercise.getRelationValues("muscles"), int_exercise.getRelationValues("muscles")) .size(); if (existingExercises.contains(int_exercise)) { continue; } if (size_intersection == 0) { break; } else { ex.setAttributeContent(this.exercise_key, int_exercise); existingExercises.add(int_exercise); break; } } } //If the dessired exercise is intermediate start with considering all the interemedite exercises } else if (new_level.getName().equalsIgnoreCase("intermediate")) { Collections.sort(intermediateExercises, comparatorEx); boolean substitution_found = false; for (OntologyElement int_exercise : intermediateExercises) { int size_intersection = CollectionUtils .intersection(exercise.getRelationValues("muscles"), int_exercise.getRelationValues("muscles")) .size(); if (existingExercises.contains(int_exercise)) { continue; } if (size_intersection == 0) { break; } else { substitution_found = true; ex.setAttributeContent(this.exercise_key, int_exercise); existingExercises.add(int_exercise); break; } } if (substitution_found) { continue; } //Then consider the begginer exercises Collections.sort(begginerExercises, comparatorEx); //Browse all the sorted begginer exercises for (OntologyElement beg_exercise : begginerExercises) { //compute the size of the intersection int size_intersection = CollectionUtils .intersection(exercise.getRelationValues("muscles"), beg_exercise.getRelationValues("muscles")) .size(); //If this exercise exist in the current planning get the next exercise if (existingExercises.contains(beg_exercise)) { continue; } //If the size_intersection consider the intermediate exercises if (size_intersection == 0) { break; } else { //Else substitut the exercise ex.setAttributeContent(this.exercise_key, beg_exercise); existingExercises.add(beg_exercise); break; } } } } } } } /** * Apply the relaxation constrains specified in the description of the new problem */ private void applyRelaxations() { //List of all muscular relaxations ArrayList<OntologyElement> muscular_groups_handicaps = this.new_case.getDescription() .getAttributeOntologies(this.relaxations_key); //Iterate over all the relaxations for (OntologyElement group : muscular_groups_handicaps) { //The planning of the actual new case ArrayList<CaseBlock> planning = this.new_case.getSolution().getAttributeComponents(this.planning_key); //All the exercises of specific muscular group ArrayList<OntologyElement> exercises_mg = StructuredOutputCBR.ontology.getElement(group.getName()) .getRelationValues("exercises"); //All the exercises of specific muscular group in the actual planning ArrayList<CaseBlock> exercises_mg_planing = this .getAllExercisesFromMuscularGroup(this.getAllExercisesFromAPlanning(planning), group); //All the combinations of exercises in muscular grups whit size exercises_mg_planing ArrayList<ArrayList<OntologyElement>> combinations = this.nCr(exercises_mg, exercises_mg_planing.size()); //Sort element with the sepecific criterium detailed in the documentation union - intersection <TIME CONSUMING!!!!!!!!!!!!!> Collections.sort(combinations, Collections.reverseOrder(new ComparatorBetweenCombinations(group))); int used_exercises = 0; //Iterate over all the exercises of a planning and substitute the ones from specifc muscular group for (int day = 0; day < this.new_case.getDescription().getAttributeInteger(this.sessions_key, 0); day++) { for (int ex = 0; ex < this.new_case.getSolution().getAttributeComponents(this.planning_key).get(day) .getAttributeComponents(this.program_key).size(); ex++) { if (this.new_case.getSolution().getAttributeComponents(this.planning_key).get(day) .getAttributeComponents(this.program_key).get(ex) .getAttributeOntology(this.exercise_key, 0).getRelationValues("muscular groups") .contains(group)) { this.new_case.getSolution().getAttributeComponents(this.planning_key).get(day) .getAttributeComponents(this.program_key).get(ex) .setAttributeContent(this.exercise_key, combinations.get(0).get(used_exercises)); used_exercises++; } } } } } /** * Apply the handicaps constrains specified in the description of the new problem */ private void applyHandicapConstrains() { //Muscular groups handicaps constrains ArrayList<OntologyElement> muscular_groups_handicaps = this.new_case.getDescription() .getAttributeOntologies(this.handicaps_key); //All the planning ArrayList<CaseBlock> planning = this.new_case.getSolution().getAttributeComponents(this.planning_key); //Iterate over all the planning and remove the exercises that use the specified muscle groups from the planning for (int day = 0; day < planning.size(); day++) { CaseBlock session = planning.get(day); ArrayList<CaseBlock> exercises = session.getAttributeComponents(this.program_key); ArrayList<Integer> to_be_removed = new ArrayList(); for (int ex_num = 0; ex_num < exercises.size(); ex_num++) { CaseBlock exercise_cb = exercises.get(ex_num); OntologyElement exercise = exercise_cb.getAttributeOntology(this.exercise_key, 0); ArrayList<OntologyElement> muscular_groups_exercise = exercise.getRelationValues("muscular groups"); for (OntologyElement ex_mg : muscular_groups_exercise) { if (muscular_groups_handicaps.contains(ex_mg)) { to_be_removed.add(ex_num); break; } } } this.new_case.getSolution().getAttributeComponents(this.planning_key).get(day) .delAttributeContents(this.program_key, to_be_removed); } } /*--------------------------------------------------------------- AUXILIAR FUNCTIONS ---------------------------------------------------------------*/ /** * Return all the case block exercises from a giving planing * @param planning An array with all the programs of a planing * @return An array with all the case blocks that represents an exercise of a planning */ private ArrayList<CaseBlock> getAllExercisesFromAPlanning(ArrayList<CaseBlock> planning) { ArrayList<CaseBlock> exercises = new ArrayList(); for (CaseBlock session : planning) { ArrayList<CaseBlock> program = session.getAttributeComponents(this.program_key); for (CaseBlock specification_program : program) { exercises.add(specification_program); } } return exercises; } private ArrayList<OntologyElement> getAllExercisesFromAPlanning_OE(ArrayList<CaseBlock> planning) { ArrayList<CaseBlock> exercises = this.getAllExercisesFromAPlanning(planning); ArrayList<OntologyElement> exercises_OE = new ArrayList(); for (CaseBlock ex : exercises) { exercises_OE.add(ex.getAttributeOntology(this.exercise_key, 0)); } return exercises_OE; } /** * Get all the exercises from a list of CaseBlock exercises from a muscular_group * @param exercises An array list of case block exercises * @param muscular_group A specific muscular group * @return all exercises of a muscular group */ private ArrayList<CaseBlock> getAllExercisesFromMuscularGroup(ArrayList<CaseBlock> exercises, OntologyElement muscular_group) { ArrayList<CaseBlock> exercises_mg = new ArrayList(); for (CaseBlock exercise : exercises) { ArrayList<OntologyElement> exe_muscular_groups = exercise.getAttributeOntology(this.exercise_key, 0) .getRelationValues("muscular groups"); for (OntologyElement exe_muscular_grup : exe_muscular_groups) if (exe_muscular_grup == muscular_group) { exercises_mg.add(exercise); } } return exercises_mg; } /** * Check if a given muscular_group is in one of the primary zones (pectorales, piernas, espalda) * @param muscular_group Muscular group to check * @return The primary zone if the muscular group is in one of them or the null if it is not */ private OntologyElement isPrimary(OntologyElement muscular_group) { if (muscular_group.getParent() == null) { if (muscular_group.getName().equalsIgnoreCase("pectorales")) { return StructuredOutputCBR.ontology.getElement("pectorales"); } else { return null; } } else { if (muscular_group.getParent().getName().equalsIgnoreCase("espalda")) { return StructuredOutputCBR.ontology.getElement("espalda"); } else if (muscular_group.getParent().getName().equalsIgnoreCase("piernas")) { return StructuredOutputCBR.ontology.getElement("piernas"); } else { return null; } } } /** * Get the session day with less exercises assigned * @param planning the current planning * @return The id of the day with less exercises assigned */ private int getIndexDayWithLessExercises(ArrayList<CaseBlock> planning) { int min_day = 0; int min_size = 9999999; for (int i = 0; i < planning.size(); i++) { CaseBlock p = planning.get(i); ArrayList<CaseBlock> program = p.getAttributeComponents(this.program_key); if (program.size() < min_size) { min_day = i; min_size = program.size(); } } return min_day; } /** * Find if a exercises implies the use of a primary zone. * @param plan The caseblock of the exercise. * @return True if implies the use of a primary zone, false otherwise */ private boolean isAPrimaryGroupExercise(CaseBlock plan) { OntologyElement exercise = plan.getAttributeOntology(this.exercise_key, 0); ArrayList<OntologyElement> ex_muscle = exercise.getRelationValues("muscular groups"); for (OntologyElement group : ex_muscle) { if (this.isPrimary(group) != null) { return true; } } return false; } /** * Fill the planning with a given set of exercises * @param type The type of a planning split or full body * @param new_exercise_set the set of exercises to use * @param new_planning a reference to the planning */ private void fillPlanning(String type, ArrayList<CaseBlock> new_exercise_set, ArrayList<CaseBlock> new_planning) { //Hash map that stores the days where are assigned the primary zone exercises in the case of full body HashMap<OntologyElement, Integer> primary_exercise_days = new HashMap(); Collections.sort(new_exercise_set, new CompareCaseBlocks()); ArrayList<OntologyElement> used_muscular_groups = new ArrayList(); used_muscular_groups.add(StructuredOutputCBR.ontology.getElement("pectorales")); used_muscular_groups.addAll(StructuredOutputCBR.ontology.getElement("espalda").getDirectChildren()); used_muscular_groups.addAll(StructuredOutputCBR.ontology.getElement("piernas").getDirectChildren()); Collections.shuffle(used_muscular_groups, new Random(42)); for (CaseBlock exercise_cb : new_exercise_set) { OntologyElement exercise = exercise_cb.getAttributeOntology(this.exercise_key, 0); used_muscular_groups = (ArrayList<OntologyElement>) CollectionUtils.union(used_muscular_groups, exercise.getRelationValues("muscular groups")); } Collections.sort(used_muscular_groups, new ComparatorMuscles()); Collections.sort(used_muscular_groups, new ComparePrimary()); //Fill the planning if is type split if (type.equalsIgnoreCase("split")) { for (OntologyElement group : used_muscular_groups) { int prefered_day = this.getIndexDayWithLessExercises(new_planning); OntologyElement primary = this.isPrimary(group); if (primary == null) { ArrayList<CaseBlock> specific_exercises = this .getAllExercisesFromMuscularGroup(new_exercise_set, group); for (CaseBlock specific_ex : specific_exercises) { if (!this.isAPrimaryGroupExercise(specific_ex)) { new_planning.get(prefered_day).addAttributeContent(this.program_key, specific_ex); new_exercise_set.remove(specific_ex); } } } else { if (primary_exercise_days.keySet().contains(primary)) { prefered_day = primary_exercise_days.get(primary); } else { boolean otherPrimaryDay = true; while (otherPrimaryDay) { prefered_day = (prefered_day + 1) % new_planning.size(); otherPrimaryDay = false; for (OntologyElement key : primary_exercise_days.keySet()) { if (primary_exercise_days.get(key) == prefered_day) { otherPrimaryDay = true; } } } } ArrayList<CaseBlock> exercises_primary = this.getAllExercisesFromMuscularGroup(new_exercise_set, group); new_planning.get(prefered_day).addAttributeContent(this.program_key, exercises_primary); new_exercise_set.removeAll(this.getAllExercisesFromMuscularGroup(new_exercise_set, group)); primary_exercise_days.put(primary, prefered_day); } } //fill the planning if is type split } else { Random rnd = new Random(42); for (OntologyElement group : used_muscular_groups) { ArrayList<CaseBlock> specific_exercises = this.getAllExercisesFromMuscularGroup(new_exercise_set, group); ArrayList<Integer> days = this.new_case.getDescription().getAttributeIntegers(this.shedule_key); Collections.shuffle(days, rnd); while (!specific_exercises.isEmpty()) { for (Integer day : days) { if (specific_exercises.isEmpty()) { break; } CaseBlock exercise = specific_exercises.remove(0); this.new_case.getSolution().getAttributeComponents(this.planning_key).get(day) .addAttributeContent(this.program_key, exercise); new_exercise_set.remove(exercise); } } } } } /** * Return a nCk of the exercises <Gosper's Hack> * @param exercises The exercises of the retrieved case. * @param k Of combinations to be generated. * @return A list of list which all possible combinations. */ private ArrayList nCr(ArrayList exercises, int k) { ArrayList result = new ArrayList(); int x = (1 << k) - 1; int limit = (1 << exercises.size()); while (x < limit) { long y = x; ArrayList combination = new ArrayList(); for (int i = Long.numberOfTrailingZeros(y); y != 0; i = Long.numberOfTrailingZeros(y)) { combination.add(exercises.get(i)); y &= ~(1 << i); } result.add(combination); int c = x & -x; int r = x + c; x = (((r ^ x) >>> 2) / c) | r; } return result; } }