gt:                 if (lec.getInstructorConstraints() != null) {
context: package org.cpsolver.coursett.criteria.additional;
  
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeMap;
  import org.cpsolver.coursett.constraint.InstructorConstraint;
  import org.cpsolver.coursett.criteria.TimePreferences;
  import org.cpsolver.coursett.criteria.TimetablingCriterion;
  import org.cpsolver.coursett.model.Lecture;
  import org.cpsolver.coursett.model.Placement;
  import org.cpsolver.ifs.assignment.Assignment;
  import org.cpsolver.ifs.util.DataProperties;
  
  /**
   * This class represent fairness criterion for instructors.
   *  Criterion iteratively evaluate instructors fairness, based on absolute 
   *  deviation of the individual satisfaction of instructors time (or time and 
   *  room) requirements.
   * @author Rostislav Burget <BurgetRostislav@gmail.com> <br>
   *         implemented criterion: Instructor Fairness <br>
   * @version CourseTT 1.3 (University Course Timetabling)<br>
   *          Copyright (C) 2015 Rostislav Burget <BurgetRostislav@gmail.com><br>
   *          <br>
   *          This library is free software; you can redistribute it and/or modify
   *          it under the terms of the GNU Lesser General Public License as
   *          published by the Free Software Foundation; either version 3 of the
   *          License, or (at your option) any later version. <br>
   *          <br>
   *          This library is distributed in the hope that it will be useful, but
   *          WITHOUT ANY WARRANTY; without even the implied warranty of
   *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   *          Lesser General Public License for more details. <br>
   *          <br>
   *          You should have received a copy of the GNU Lesser General Public
   *          License along with this library; if not see
   *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/
   *          </a>.
   */
  public class InstructorFairness extends TimetablingCriterion {
  
      /**
       *
       */
      public InstructorFairness() {
          setValueUpdateType(ValueUpdateType.BeforeUnassignedAfterAssigned);
      }
  
      @Override
      public double getWeightDefault(DataProperties config) {
          return config.getPropertyDouble("Comparator.InstructorFairnessPreferenceWeight", 1.0);
      }
  
      @Override
      public String getPlacementSelectionWeightName() {
          return "Placement.InstructorFairnessPreferenceWeight";
      }
  
      @Override
      public void bestSaved(Assignment<Lecture, Placement> assignment) {
          iBest = getValue(assignment);
      }
  
      @Override
      public double getValue(Assignment<Lecture, Placement> assignment, Placement value, Set<Placement> conflicts) {
          double ret = 0.0;
          InstructorFairnessContext context = (InstructorFairnessContext) getContext(assignment);
          if (context.allInstructorsAssigned(assignment) && !value.variable().getInstructorConstraints().isEmpty()) {
              List<InstructorConstraint> insConstraints = value.variable().getInstructorConstraints();
              ret = (context.getDiffInstrValue(insConstraints, context.fairnessDouble(assignment, value))) / insConstraints.size();
              if (conflicts != null) {
                  for (Placement conflict : conflicts) {
                      if (!conflict.variable().getInstructorConstraints().isEmpty()) {
                          List<InstructorConstraint> insConstraints2 = conflict.variable().getInstructorConstraints();
                          ret -= (context.getDiffInstrValue(insConstraints2, context.fairnessDouble(assignment, conflict))) / insConstraints2.size();
                      }
                  }
              }
          }
          return ret;
      }
  
      @Override
      public double getValue(Assignment<Lecture, Placement> assignment) {
          InstructorFairnessContext context = (InstructorFairnessContext) getContext(assignment);
          if (context.allInstructorsAssigned(assignment))
              return context.getObjectiveValue();
          return 0.0;
      }
  
      @Override
      public double getValue(Assignment<Lecture, Placement> assignment, Collection<Lecture> variables) {
          InstructorFairnessContext context = (InstructorFairnessContext) getContext(assignment);
          if (context.allInstructorsAssigned(assignment)) {
              Set<InstructorConstraint> constraints = new HashSet<InstructorConstraint>();
              for (Lecture lecture : variables) {
                  constraints.addAll(lecture.getInstructorConstraints());
              }
              return context.getObjectiveValue(constraints);
          }
          return 0.0;
      }
  
      @Override
      public void getInfo(Assignment<Lecture, Placement> assignment, Map<String, String> info) {
          double value = getValue(assignment);
          if (value != 0.0)
              info.put(getName(), sDoubleFormat.format(value));
      }
  
      @Override
      public void getInfo(Assignment<Lecture, Placement> assignment, Map<String, String> info, Collection<Lecture> variables) {
          double value = getValue(assignment, variables);
          if (value != 0.0)
              info.put(getName(), sDoubleFormat.format(value));
      }
  
      @Override
      public void getExtendedInfo(Assignment<Lecture, Placement> assignment, Map<String, String> info) {
          if (iDebug) {
              InstructorFairnessContext context = (InstructorFairnessContext) getContext(assignment);
              String[] fairnessInfo = context.testFairness(assignment);
              info.put(getName() + " Details",
                      fairnessInfo[8] + " (avg: " + fairnessInfo[0] + ", rms: " + fairnessInfo[1] +
                      ", Pmax: " + fairnessInfo[2] + ", Pdev: " + fairnessInfo[3] + ", Perror: " + fairnessInfo[4] +
                      ", Pss: " + fairnessInfo[5] + ", Jain's index: " + fairnessInfo[6] + ", max: " + fairnessInfo[7] + ")");
          }
      }
  
      /**
       * Context for InstructorFairness
       */
      public class InstructorFairnessContext extends ValueContext {
          private TreeMap<Long, Instructor> iId2Instructor = new TreeMap<Long, Instructor>();
          private double iInstrMeanFairValue = 0.0;
          private boolean iFullTreeMap = false;
          private boolean iFirstIterDone = false;
  
          /**
           * 
           * @param assignment
           *            current assignment
           */
          public InstructorFairnessContext(Assignment<Lecture, Placement> assignment) {
              countInstructorFair(assignment);
          }
  
          @Override
          protected void assigned(Assignment<Lecture, Placement> assignment, Placement value) {
              if (isFirstIterDone()) {
                  countInstructorAssignedFair(assignment, value);
              } else {
                  countInstructorFair(assignment);
              }
          }
  
          @Override
          protected void unassigned(Assignment<Lecture, Placement> assignment, Placement value) {
              if (isFirstIterDone()) {
                  countInstructorUnassignedFair(assignment, value);
              } else {
                  if (countInstructorFair(assignment))
                      countInstructorUnassignedFair(assignment, value);
              }
          }
  
          /**
           * This method set fairness values to all instructors
           * 
           * @param assignment
           * @return false if complete solution wasn't found
           */
          public boolean countInstructorFair(Assignment<Lecture, Placement> assignment) {
              if (allInstructorsAssigned(assignment)) {
                  iId2Instructor.clear();
                  for (Lecture lecture : getModel().variables()) {
                      Double bestPossibleValue = null;
  
                      for (Placement t : lecture.values(assignment)) {
                          double f = fairnessDouble(assignment, t);
                          if (bestPossibleValue == null || f < bestPossibleValue)
                              bestPossibleValue = f;
                      }
  
                      Placement placement = assignment.getValue(lecture);
                      for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
                          Instructor s = iId2Instructor.get(ic.getResourceId());
                          if (s == null) {
                              s = new Instructor(ic.getResourceId());
                              iId2Instructor.put(ic.getResourceId(), s);
                          }
                          if (bestPossibleValue != null)
                              s.addBestValue(bestPossibleValue);
                          if (placement != null) {
                              s.addValue(fairnessDouble(assignment, placement));
                              s.incNumOfClasses();
                          }
                      }
                  }
                  countInstrMeanFairValue();
                  setFirstIterDone();
                  return true;
              } else {
                  return false;
              }
          }
  
          /**
           * Method actualize values of instructors whose lecture was just
           * assigned
           * 
           * @param assignment 
           * @param value placement of lecture
           */
  
          public void countInstructorAssignedFair(Assignment<Lecture, Placement> assignment, Placement value) {
              Lecture lec = value.variable();
              if (lec.getInstructorConstraints() != null) {
                  List<InstructorConstraint> insConstraints = lec.getInstructorConstraints();
                  double critValue = fairnessDouble(assignment, lec.getAssignment(assignment));
                  for (InstructorConstraint ic : insConstraints) {
                      if (!addInstructorValue(ic.getResourceId(), critValue)) {
                          throw new IllegalArgumentException("Instructor " + ic.getResourceId() + " is not present in the context.");
                      }
                  }
              }
              countInstrMeanFairValue();
          }
  
          /**
           * Method actualize values of instructors whose lecture will be
           * unassigned
           * 
           * @param assignment
           * @param value placement of lecture
           */
  
          public void countInstructorUnassignedFair(Assignment<Lecture, Placement> assignment, Placement value) {
              Lecture lec = value.variable();
              if (lec.getInstructorConstraints() != null) {
                  List<InstructorConstraint> insConstraints = lec.getInstructorConstraints();
                  double critValue = fairnessDouble(assignment, lec.getAssignment(assignment));
                  for (InstructorConstraint ic : insConstraints) {
                      if (!decreaseInstructorValue(ic.getResourceId(), critValue)) {
                          throw new IllegalArgumentException("Instructor " + ic.getResourceId() + " is not present in the context.");
                      }
                  }
              }
              countInstrMeanFairValue();
          }
  
          /**
           *
           * @return instructor mean fair value 
           */
          public double getInstrMeanFairValue() {
              return iInstrMeanFairValue;
          }
  
          /**
           *
           * @return if first iteration was done
           */
          public boolean isFirstIterDone() {
              return iFirstIterDone;
          }
  
          /**
           * set first iteration done to true
           */
          public void setFirstIterDone() {
              this.iFirstIterDone = true;
          }
  
          /**
           *
           * @return number of instructors
           */
          public int getNumOfIstructors() {
              return iId2Instructor.size();
          }
  
          /**
           *
           * @return Collection of instructors in context
           */
          public Collection<Instructor> getInstructorsWithAssig() {
              return iId2Instructor.values();
          }
  
          /**
           * Return if complete solution (all variables assigned) was found 
           * in this context
           * @param assignment
           * @return true if in this context were all variables assigned 
           * false otherwise
           */
          public boolean allInstructorsAssigned(Assignment<Lecture, Placement> assignment) {
              if (!iFullTreeMap) {
                  iFullTreeMap = (assignment.nrAssignedVariables() > 0 && assignment.nrUnassignedVariables(getModel()) == 0 && getModel().getBestUnassignedVariables() == 0);
              }
              return iFullTreeMap;
          }
  
          /**
           * adding value to instructor in stringInstMap
           *
           * @param insID instructor ID
           * @param value that should be added
           * @return false if instructor is not in iId2Instructor
           */
  
          public boolean addInstructorValue(Long insID, double value) {
              Instructor s = iId2Instructor.get(insID);
              if (s != null) {
                  s.addValue(value);
                  s.incNumOfClasses();
                  return true;
              } else {
                  return false;
              }
          }
  
          /**
           * adding value to instructor in stringInstMap
           * 
           * @param insID instructor ID
           * @param value value that should be subtracted
           * @return false if instructor is not iId2Instructor
           */
  
          public boolean decreaseInstructorValue(Long insID, double value) {
              Instructor s = iId2Instructor.get(insID);
              if (s != null) {
                  s.removeValue(value);
                  s.decNumOfClasses();
                  return true;
              } else {
                  return false;
              }
          }
  
          /**
           * compute and return mean fair value of instructors in iId2Instructor
           */
  
          public void countInstrMeanFairValue() {
              if (!iId2Instructor.isEmpty()) {
                  double sum = 0.0;
                  for (Instructor ins: iId2Instructor.values()) {
                      sum += ins.getFinalValue();
                  }
                  iInstrMeanFairValue = sum / iId2Instructor.size();
              }
          }
  
          /**
           * Method estimates value of placement for instructors in entry list
           * 
           * @param instructorsList
           * @param placementValue
           * @return estimated value of placement for instructors in entry list
           */
  
          public double getDiffInstrValue(List<InstructorConstraint> instructorsList, double placementValue) {
              double ret = 0.0;
              for (InstructorConstraint ic : instructorsList) {
                  Instructor i = iId2Instructor.get(ic.getResourceId());
                  if (i != null) {
                      if (i.getFinalValue() > iInstrMeanFairValue) {
                          ret += ((i.getFinalValue() - iInstrMeanFairValue) / i.getNumOfClasses()) + (placementValue - iInstrMeanFairValue);
                      } else {
                          ret -= ((iInstrMeanFairValue - i.getFinalValue()) / i.getNumOfClasses()) + (iInstrMeanFairValue - placementValue);
                      }
                  }
              }
              return ret;
          }
  
          /**
           * Fairness value based on pdev (pdev sec. part) of all instructors
           * 
           * @return Objective value of all instructors
           */
  
          public double getObjectiveValue() {
              double ret = 0.0;
              for (Map.Entry<Long, Instructor> entry : iId2Instructor.entrySet()) {
                  Instructor ins = entry.getValue();
                  ret += Math.abs(ins.getFinalValue() - iInstrMeanFairValue);
              }
              return ret;
          }
  
          /**
           * Fairness value based on pdev (pdev sec. part) of instructors
           * @param instructors
           * @return Objective value of instructors 
           */
          public double getObjectiveValue(Collection<InstructorConstraint> instructors) {
              double ret = 0.0;
              for (InstructorConstraint ic : instructors) {
                  Instructor ins = iId2Instructor.get(ic.getResourceId());
                  if (ins != null)
                      ret += Math.abs(ins.getFinalValue() - iInstrMeanFairValue);
              }
              return ret;
          }
  
          /**
           * fairness value with squared P and avg.P
           * 
           * @return fairness value with squared P and avg.P
           */
  
          public double getDiffAllInstrValueSquared() {
              double ret = 0.0;
              for (Map.Entry<Long, Instructor> entry : iId2Instructor.entrySet()) {
                  Instructor ins = entry.getValue();
                  ret += Math.sqrt(Math.abs(ins.getFinalValue() * ins.getFinalValue() - iInstrMeanFairValue * iInstrMeanFairValue));
              }
              return ret;
          }
  
          /**
           * refresh of all instructors in iId2Instructor
           *
           */
  
          public void refreshInstructors() {
              for (Map.Entry<Long, Instructor> entry : iId2Instructor.entrySet()) {
                  Instructor ins = entry.getValue();
                  ins.refresh();
              }
          }
  
          /**
           * Metod count and return time (and room) preferences in placement
           *
           * @param assignment 
           * @param placement 
           * @return time (and room) preferences in placement
           */
  
          public double fairnessDouble(Assignment<Lecture, Placement> assignment, Placement placement) {
              double critValue = 0.0;
              // critValue += getModel().getCriterion(RoomPreferences.class).getWeightedValue(assignment,placement,null);
              critValue += getModel().getCriterion(TimePreferences.class).getWeightedValue(assignment, placement, null);
              return critValue;
          }
  
          /**
           * Method for whole evaluation of fairness criteria
           * 
           * @param assignment
           * @return String[] with informations about solution [0-Avarage
           *         instructor penalty, 1-Sum of squared penalities, 2-Pmax,
           *         3-Pdev, 4-Perror, 5-Pss, 6-Jain, 7-worst instructor fairness
           *         value,8-Instructors fairness value]
           */
  
          public String[] testFairness(Assignment<Lecture, Placement> assignment) {
              String[] dataForEval = new String[9];
              Collection<Lecture> assignedLectures = assignment.assignedVariables();
              refreshInstructors();
              for (Lecture lec : assignedLectures) {
  