001package org.cpsolver.studentsct.model; 002 003import java.text.DecimalFormat; 004import java.util.ArrayList; 005import java.util.HashMap; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Map; 009import java.util.Set; 010 011import org.cpsolver.coursett.model.Placement; 012import org.cpsolver.coursett.model.RoomLocation; 013import org.cpsolver.coursett.model.TimeLocation; 014import org.cpsolver.ifs.assignment.Assignment; 015import org.cpsolver.ifs.assignment.AssignmentComparable; 016import org.cpsolver.ifs.assignment.context.AbstractClassWithContext; 017import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; 018import org.cpsolver.ifs.model.Model; 019import org.cpsolver.studentsct.reservation.Reservation; 020 021 022/** 023 * Representation of a class. Each section contains id, name, scheduling 024 * subpart, time/room placement, and a limit. Optionally, parent-child relation 025 * between sections can be defined. <br> 026 * <br> 027 * Each student requesting a course needs to be enrolled in a class of each 028 * subpart of a selected configuration. In the case of parent-child relation 029 * between classes, if a student is enrolled in a section that has a parent 030 * section defined, he/she has to be enrolled in the parent section as well. If 031 * there is a parent-child relation between two sections, the same relation is 032 * defined on their subparts as well, i.e., if section A is a parent section B, 033 * subpart of section A isa parent of subpart of section B. <br> 034 * <br> 035 * 036 * @version StudentSct 1.3 (Student Sectioning)<br> 037 * Copyright (C) 2007 - 2014 Tomas Muller<br> 038 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 039 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 040 * <br> 041 * This library is free software; you can redistribute it and/or modify 042 * it under the terms of the GNU Lesser General Public License as 043 * published by the Free Software Foundation; either version 3 of the 044 * License, or (at your option) any later version. <br> 045 * <br> 046 * This library is distributed in the hope that it will be useful, but 047 * WITHOUT ANY WARRANTY; without even the implied warranty of 048 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 049 * Lesser General Public License for more details. <br> 050 * <br> 051 * You should have received a copy of the GNU Lesser General Public 052 * License along with this library; if not see 053 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 054 */ 055public class Section extends AbstractClassWithContext<Request, Enrollment, Section.SectionContext> implements SctAssignment, AssignmentComparable<Section, Request, Enrollment> { 056 private static DecimalFormat sDF = new DecimalFormat("0.000"); 057 private long iId = -1; 058 private String iName = null; 059 private Map<Long, String> iNameByCourse = null; 060 private Subpart iSubpart = null; 061 private Section iParent = null; 062 private Placement iPlacement = null; 063 private int iLimit = 0; 064 private Choice iChoice = null; 065 private double iPenalty = 0.0; 066 private double iSpaceExpected = 0.0; 067 private double iSpaceHeld = 0.0; 068 private String iNote = null; 069 private Set<Long> iIgnoreConflictsWith = null; 070 071 /** 072 * Constructor 073 * 074 * @param id 075 * section unique id 076 * @param limit 077 * section limit, i.e., the maximal number of students that can 078 * be enrolled in this section at the same time 079 * @param name 080 * section name 081 * @param subpart 082 * subpart of this section 083 * @param placement 084 * time/room placement 085 * @param instructorIds 086 * instructor(s) id -- needed for {@link Section#getChoice()} 087 * @param instructorNames 088 * instructor(s) name -- needed for {@link Section#getChoice()} 089 * @param parent 090 * parent section -- if there is a parent section defined, a 091 * student that is enrolled in this section has to be enrolled in 092 * the parent section as well. Also, the same relation needs to 093 * be defined between subpart of this section and the subpart of 094 * the parent section 095 */ 096 public Section(long id, int limit, String name, Subpart subpart, Placement placement, String instructorIds, 097 String instructorNames, Section parent) { 098 iId = id; 099 iLimit = limit; 100 iName = name; 101 iSubpart = subpart; 102 iSubpart.getSections().add(this); 103 iPlacement = placement; 104 iParent = parent; 105 iChoice = new Choice(getSubpart().getConfig().getOffering(), getSubpart().getInstructionalType(), getTime(), 106 instructorIds, instructorNames); 107 } 108 109 /** Section id */ 110 @Override 111 public long getId() { 112 return iId; 113 } 114 115 /** 116 * Section limit. This is defines the maximal number of students that can be 117 * enrolled into this section at the same time. It is -1 in the case of an 118 * unlimited section 119 * @return class limit 120 */ 121 public int getLimit() { 122 return iLimit; 123 } 124 125 /** Set section limit 126 * @param limit class limit 127 **/ 128 public void setLimit(int limit) { 129 iLimit = limit; 130 } 131 132 /** Section name 133 * @return class name 134 **/ 135 public String getName() { 136 return iName; 137 } 138 139 /** Set section name 140 * @param name class name 141 **/ 142 public void setName(String name) { 143 iName = name; 144 } 145 146 /** Scheduling subpart to which this section belongs 147 * @return scheduling subpart 148 **/ 149 public Subpart getSubpart() { 150 return iSubpart; 151 } 152 153 /** 154 * Parent section of this section (can be null). If there is a parent 155 * section defined, a student that is enrolled in this section has to be 156 * enrolled in the parent section as well. Also, the same relation needs to 157 * be defined between subpart of this section and the subpart of the parent 158 * section. 159 * @return parent class 160 */ 161 public Section getParent() { 162 return iParent; 163 } 164 165 /** 166 * Time/room placement of the section. This can be null, for arranged 167 * sections. 168 * @return time and room assignment of this class 169 */ 170 public Placement getPlacement() { 171 return iPlacement; 172 } 173 174 /** 175 * Set time/room placement of the section. This can be null, for arranged 176 * sections. 177 * @param placement time and room assignment of this class 178 */ 179 public void setPlacement(Placement placement) { 180 iPlacement = placement; 181 } 182 183 /** Time placement of the section. */ 184 @Override 185 public TimeLocation getTime() { 186 return (iPlacement == null ? null : iPlacement.getTimeLocation()); 187 } 188 189 /** Number of rooms in which the section meet. */ 190 @Override 191 public int getNrRooms() { 192 return (iPlacement == null ? 0 : iPlacement.getNrRooms()); 193 } 194 195 /** 196 * Room placement -- list of 197 * {@link org.cpsolver.coursett.model.RoomLocation} 198 */ 199 @Override 200 public List<RoomLocation> getRooms() { 201 if (iPlacement == null) 202 return null; 203 if (iPlacement.getRoomLocations() == null && iPlacement.getRoomLocation() != null) { 204 List<RoomLocation> ret = new ArrayList<RoomLocation>(1); 205 ret.add(iPlacement.getRoomLocation()); 206 return ret; 207 } 208 return iPlacement.getRoomLocations(); 209 } 210 211 /** 212 * True, if this section overlaps with the given assignment in time and 213 * space 214 */ 215 @Override 216 public boolean isOverlapping(SctAssignment assignment) { 217 if (isAllowOverlap() || assignment.isAllowOverlap()) return false; 218 if (getTime() == null || assignment.getTime() == null) return false; 219 if (assignment instanceof Section && isToIgnoreStudentConflictsWith(assignment.getId())) return false; 220 return getTime().hasIntersection(assignment.getTime()); 221 } 222 223 /** 224 * True, if this section overlaps with one of the given set of assignments 225 * in time and space 226 */ 227 @Override 228 public boolean isOverlapping(Set<? extends SctAssignment> assignments) { 229 if (isAllowOverlap()) return false; 230 if (getTime() == null || assignments == null) 231 return false; 232 for (SctAssignment assignment : assignments) { 233 if (assignment.isAllowOverlap()) 234 continue; 235 if (assignment.getTime() == null) 236 continue; 237 if (assignment instanceof Section && isToIgnoreStudentConflictsWith(assignment.getId())) 238 continue; 239 if (getTime().hasIntersection(assignment.getTime())) 240 return true; 241 } 242 return false; 243 } 244 245 /** Called when an enrollment with this section is assigned to a request */ 246 @Override 247 public void assigned(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 248 getContext(assignment).assigned(assignment, enrollment); 249 } 250 251 /** Called when an enrollment with this section is unassigned from a request */ 252 @Override 253 public void unassigned(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 254 getContext(assignment).unassigned(assignment, enrollment); 255 } 256 257 /** Long name: subpart name + time long name + room names + instructor names 258 * @param useAmPm use 12-hour format 259 * @return long name 260 **/ 261 public String getLongName(boolean useAmPm) { 262 return getSubpart().getName() + " " + getName() + " " + (getTime() == null ? "" : " " + getTime().getLongName(useAmPm)) 263 + (getNrRooms() == 0 ? "" : " " + getPlacement().getRoomName(",")) 264 + (getChoice().getInstructorNames() == null ? "" : " " + getChoice().getInstructorNames()); 265 } 266 267 @Deprecated 268 public String getLongName() { 269 return getLongName(true); 270 } 271 272 @Override 273 public String toString() { 274 return getSubpart().getConfig().getOffering().getName() + " " + getSubpart().getName() + " " + getName() 275 + (getTime() == null ? "" : " " + getTime().getLongName(true)) 276 + (getNrRooms() == 0 ? "" : " " + getPlacement().getRoomName(",")) 277 + (getChoice().getInstructorNames() == null ? "" : " " + getChoice().getInstructorNames()) + " (L:" 278 + (getLimit() < 0 ? "unlimited" : "" + getLimit()) 279 + (getPenalty() == 0.0 ? "" : ",P:" + sDF.format(getPenalty())) + ")"; 280 } 281 282 /** A (student) choice representing this section. 283 * @return choice for this class 284 **/ 285 public Choice getChoice() { 286 return iChoice; 287 } 288 289 /** 290 * Return penalty which is added to an enrollment that contains this 291 * section. 292 * @return online penalty 293 */ 294 public double getPenalty() { 295 return iPenalty; 296 } 297 298 /** Set penalty which is added to an enrollment that contains this section. 299 * @param penalty online penalty 300 **/ 301 public void setPenalty(double penalty) { 302 iPenalty = penalty; 303 } 304 305 /** 306 * Compare two sections, prefer sections with lower penalty and more open 307 * space 308 */ 309 @Override 310 public int compareTo(Assignment<Request, Enrollment> assignment, Section s) { 311 int cmp = Double.compare(getPenalty(), s.getPenalty()); 312 if (cmp != 0) 313 return cmp; 314 cmp = Double.compare(getLimit() - getContext(assignment).getEnrollmentWeight(assignment, null), s.getLimit() - s.getContext(assignment).getEnrollmentWeight(assignment, null)); 315 if (cmp != 0) 316 return cmp; 317 return Double.compare(getId(), s.getId()); 318 } 319 320 /** 321 * Compare two sections, prefer sections with lower penalty 322 */ 323 @Override 324 public int compareTo(Section s) { 325 int cmp = Double.compare(getPenalty(), s.getPenalty()); 326 if (cmp != 0) 327 return cmp; 328 return Double.compare(getId(), s.getId()); 329 } 330 331 /** 332 * Return the amount of space of this section that is held for incoming 333 * students. This attribute is computed during the batch sectioning (it is 334 * the overall weight of dummy students enrolled in this section) and it is 335 * being updated with each incomming student during the online sectioning. 336 * @return space held 337 */ 338 public double getSpaceHeld() { 339 return iSpaceHeld; 340 } 341 342 /** 343 * Set the amount of space of this section that is held for incoming 344 * students. See {@link Section#getSpaceHeld()} for more info. 345 * @param spaceHeld space held 346 */ 347 public void setSpaceHeld(double spaceHeld) { 348 iSpaceHeld = spaceHeld; 349 } 350 351 /** 352 * Return the amount of space of this section that is expected to be taken 353 * by incoming students. This attribute is computed during the batch 354 * sectioning (for each dummy student that can attend this section (without 355 * any conflict with other enrollments of that student), 1 / x where x is 356 * the number of such sections of this subpart is added to this value). 357 * Also, this value is being updated with each incoming student during the 358 * online sectioning. 359 * @return space expected 360 */ 361 public double getSpaceExpected() { 362 return iSpaceExpected; 363 } 364 365 /** 366 * Set the amount of space of this section that is expected to be taken by 367 * incoming students. See {@link Section#getSpaceExpected()} for more info. 368 * @param spaceExpected space expected 369 */ 370 public void setSpaceExpected(double spaceExpected) { 371 iSpaceExpected = spaceExpected; 372 } 373 374 /** 375 * Online sectioning penalty. 376 * @param assignment current assignment 377 * @return online sectioning penalty 378 */ 379 public double getOnlineSectioningPenalty(Assignment<Request, Enrollment> assignment) { 380 if (getLimit() <= 0) 381 return 0.0; 382 383 double available = getLimit() - getContext(assignment).getEnrollmentWeight(assignment, null); 384 385 double penalty = (getSpaceExpected() - available) / getLimit(); 386 387 return Math.max(-1.0, Math.min(1.0, penalty)); 388 } 389 390 /** 391 * Return true if overlaps are allowed, but the number of overlapping slots should be minimized. 392 * This can be changed on the subpart, using {@link Subpart#setAllowOverlap(boolean)}. 393 **/ 394 @Override 395 public boolean isAllowOverlap() { 396 return iSubpart.isAllowOverlap(); 397 } 398 399 /** Sections first, then by {@link FreeTimeRequest#getId()} */ 400 @Override 401 public int compareById(SctAssignment a) { 402 if (a instanceof Section) { 403 return new Long(getId()).compareTo(((Section)a).getId()); 404 } else { 405 return -1; 406 } 407 } 408 409 /** 410 * Available space in the section that is not reserved by any section reservation 411 * @param assignment current assignment 412 * @param excludeRequest excluding given request (if not null) 413 * @return unreserved space in this class 414 **/ 415 public double getUnreservedSpace(Assignment<Request, Enrollment> assignment, Request excludeRequest) { 416 // section is unlimited -> there is unreserved space unless there is an unlimited reservation too 417 // (in which case there is no unreserved space) 418 if (getLimit() < 0) { 419 // exclude reservations that are not directly set on this section 420 for (Reservation r: getSectionReservations()) { 421 // ignore expired reservations 422 if (r.isExpired()) continue; 423 // there is an unlimited reservation -> no unreserved space 424 if (r.getLimit() < 0) return 0.0; 425 } 426 return Double.MAX_VALUE; 427 } 428 429 double available = getLimit() - getContext(assignment).getEnrollmentWeight(assignment, excludeRequest); 430 // exclude reservations that are not directly set on this section 431 for (Reservation r: getSectionReservations()) { 432 // ignore expired reservations 433 if (r.isExpired()) continue; 434 // unlimited reservation -> all the space is reserved 435 if (r.getLimit() < 0.0) return 0.0; 436 // compute space that can be potentially taken by this reservation 437 double reserved = r.getContext(assignment).getReservedAvailableSpace(assignment, excludeRequest); 438 // deduct the space from available space 439 available -= Math.max(0.0, reserved); 440 } 441 442 return available; 443 } 444 445 /** 446 * Total space in the section that cannot be used by any section reservation 447 * @return total unreserved space in this class 448 **/ 449 public double getTotalUnreservedSpace() { 450 if (iTotalUnreservedSpace == null) 451 iTotalUnreservedSpace = getTotalUnreservedSpaceNoCache(); 452 return iTotalUnreservedSpace; 453 } 454 private Double iTotalUnreservedSpace = null; 455 private double getTotalUnreservedSpaceNoCache() { 456 // section is unlimited -> there is unreserved space unless there is an unlimited reservation too 457 // (in which case there is no unreserved space) 458 if (getLimit() < 0) { 459 // exclude reservations that are not directly set on this section 460 for (Reservation r: getSectionReservations()) { 461 // ignore expired reservations 462 if (r.isExpired()) continue; 463 // there is an unlimited reservation -> no unreserved space 464 if (r.getLimit() < 0) return 0.0; 465 } 466 return Double.MAX_VALUE; 467 } 468 469 // we need to check all reservations linked with this section 470 double available = getLimit(), reserved = 0, exclusive = 0; 471 Set<Section> sections = new HashSet<Section>(); 472 reservations: for (Reservation r: getSectionReservations()) { 473 // ignore expired reservations 474 if (r.isExpired()) continue; 475 // unlimited reservation -> no unreserved space 476 if (r.getLimit() < 0) return 0.0; 477 for (Section s: r.getSections(getSubpart())) { 478 if (s.equals(this)) continue; 479 if (s.getLimit() < 0) continue reservations; 480 if (sections.add(s)) 481 available += s.getLimit(); 482 } 483 reserved += r.getLimit(); 484 if (r.getSections(getSubpart()).size() == 1) 485 exclusive += r.getLimit(); 486 } 487 488 return Math.min(available - reserved, getLimit() - exclusive); 489 } 490 491 492 /** 493 * Get reservations for this section 494 * @return reservations that can use this class 495 */ 496 public List<Reservation> getReservations() { 497 if (iReservations == null) { 498 iReservations = new ArrayList<Reservation>(); 499 for (Reservation r: getSubpart().getConfig().getOffering().getReservations()) { 500 if (r.getSections(getSubpart()) == null || r.getSections(getSubpart()).contains(this)) 501 iReservations.add(r); 502 } 503 } 504 return iReservations; 505 } 506 private List<Reservation> iReservations = null; 507 508 /** 509 * Get reservations that require this section 510 * @return reservations that must use this class 511 */ 512 public List<Reservation> getSectionReservations() { 513 if (iSectionReservations == null) { 514 iSectionReservations = new ArrayList<Reservation>(); 515 for (Reservation r: getSubpart().getSectionReservations()) { 516 if (r.getSections(getSubpart()).contains(this)) 517 iSectionReservations.add(r); 518 } 519 } 520 return iSectionReservations; 521 } 522 private List<Reservation> iSectionReservations = null; 523 524 /** 525 * Clear reservation information that was cached on this section 526 */ 527 public void clearReservationCache() { 528 iReservations = null; 529 iSectionReservations = null; 530 iTotalUnreservedSpace = null; 531 } 532 533 /** 534 * Return course-dependent section name 535 * @param courseId course offering unique id 536 * @return course dependent class name 537 */ 538 public String getName(long courseId) { 539 if (iNameByCourse == null) return getName(); 540 String name = iNameByCourse.get(courseId); 541 return (name == null ? getName() : name); 542 } 543 544 /** 545 * Set course-dependent section name 546 * @param courseId course offering unique id 547 * @param name course dependent class name 548 */ 549 public void setName(long courseId, String name) { 550 if (iNameByCourse == null) iNameByCourse = new HashMap<Long, String>(); 551 iNameByCourse.put(courseId, name); 552 } 553 554 /** 555 * Return course-dependent section names 556 * @return map of course-dependent class names 557 */ 558 public Map<Long, String> getNameByCourse() { return iNameByCourse; } 559 560 @Override 561 public boolean equals(Object o) { 562 if (o == null || !(o instanceof Section)) return false; 563 return getId() == ((Section)o).getId(); 564 } 565 566 @Override 567 public int hashCode() { 568 return (int) (iId ^ (iId >>> 32)); 569 } 570 571 /** 572 * Section note 573 * @return scheduling note 574 */ 575 public String getNote() { return iNote; } 576 577 /** 578 * Section note 579 * @param note scheduling note 580 */ 581 public void setNote(String note) { iNote = note; } 582 583 /** 584 * Add section id of a section that student conflicts are to be ignored with 585 * @param sectionId class unique id 586 */ 587 public void addIgnoreConflictWith(long sectionId) { 588 if (iIgnoreConflictsWith == null) iIgnoreConflictsWith = new HashSet<Long>(); 589 iIgnoreConflictsWith.add(sectionId); 590 } 591 592 /** 593 * Returns true if student conflicts between this section and the given one are to be ignored 594 * @param sectionId class unique id 595 * @return true if student conflicts between these two sections are to be ignored 596 */ 597 public boolean isToIgnoreStudentConflictsWith(long sectionId) { 598 return iIgnoreConflictsWith != null && iIgnoreConflictsWith.contains(sectionId); 599 } 600 601 /** 602 * Returns a set of ids of sections that student conflicts are to be ignored with (between this section and the others) 603 * @return set of class unique ids of the sections that student conflicts are to be ignored with 604 */ 605 public Set<Long> getIgnoreConflictWithSectionIds() { 606 return iIgnoreConflictsWith; 607 } 608 609 /** Set of assigned enrollments */ 610 @Override 611 public Set<Enrollment> getEnrollments(Assignment<Request, Enrollment> assignment) { 612 return getContext(assignment).getEnrollments(); 613 } 614 615 /** 616 * Enrollment weight -- weight of all requests which have an enrollment that 617 * contains this section, excluding the given one. See 618 * {@link Request#getWeight()}. 619 * @param assignment current assignment 620 * @param excludeRequest course request to ignore, if any 621 * @return enrollment weight 622 */ 623 public double getEnrollmentWeight(Assignment<Request, Enrollment> assignment, Request excludeRequest) { 624 return getContext(assignment).getEnrollmentWeight(assignment, excludeRequest); 625 } 626 627 /** 628 * Maximal weight of a single enrollment in the section 629 * @param assignment current assignment 630 * @return maximal enrollment weight 631 */ 632 public double getMaxEnrollmentWeight(Assignment<Request, Enrollment> assignment) { 633 return getContext(assignment).getMaxEnrollmentWeight(); 634 } 635 636 /** 637 * Minimal weight of a single enrollment in the section 638 * @param assignment current assignment 639 * @return minimal enrollment weight 640 */ 641 public double getMinEnrollmentWeight(Assignment<Request, Enrollment> assignment) { 642 return getContext(assignment).getMinEnrollmentWeight(); 643 } 644 645 @Override 646 public Model<Request, Enrollment> getModel() { 647 return getSubpart().getConfig().getOffering().getModel(); 648 } 649 650 @Override 651 public SectionContext createAssignmentContext(Assignment<Request, Enrollment> assignment) { 652 return new SectionContext(assignment); 653 } 654 655 public class SectionContext implements AssignmentConstraintContext<Request, Enrollment> { 656 private Set<Enrollment> iEnrollments = new HashSet<Enrollment>(); 657 private double iEnrollmentWeight = 0.0; 658 private double iMaxEnrollmentWeight = 0.0; 659 private double iMinEnrollmentWeight = 0.0; 660 661 public SectionContext(Assignment<Request, Enrollment> assignment) { 662 for (Course course: getSubpart().getConfig().getOffering().getCourses()) { 663 for (CourseRequest request: course.getRequests()) { 664 Enrollment enrollment = assignment.getValue(request); 665 if (enrollment != null && enrollment.getSections().contains(Section.this)) 666 assigned(assignment, enrollment); 667 } 668 } 669 } 670 671 /** Called when an enrollment with this section is assigned to a request */ 672 @Override 673 public void assigned(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 674 if (iEnrollments.isEmpty()) { 675 iMinEnrollmentWeight = iMaxEnrollmentWeight = enrollment.getRequest().getWeight(); 676 } else { 677 iMaxEnrollmentWeight = Math.max(iMaxEnrollmentWeight, enrollment.getRequest().getWeight()); 678 iMinEnrollmentWeight = Math.min(iMinEnrollmentWeight, enrollment.getRequest().getWeight()); 679 } 680 if (iEnrollments.add(enrollment)) 681 iEnrollmentWeight += enrollment.getRequest().getWeight(); 682 } 683 684 /** Called when an enrollment with this section is unassigned from a request */ 685 @Override 686 public void unassigned(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 687 if (iEnrollments.remove(enrollment)) 688 iEnrollmentWeight -= enrollment.getRequest().getWeight(); 689 if (iEnrollments.isEmpty()) { 690 iMinEnrollmentWeight = iMaxEnrollmentWeight = 0; 691 } else if (iMinEnrollmentWeight != iMaxEnrollmentWeight) { 692 if (iMinEnrollmentWeight == enrollment.getRequest().getWeight()) { 693 double newMinEnrollmentWeight = Double.MAX_VALUE; 694 for (Enrollment e : iEnrollments) { 695 if (e.getRequest().getWeight() == iMinEnrollmentWeight) { 696 newMinEnrollmentWeight = iMinEnrollmentWeight; 697 break; 698 } else { 699 newMinEnrollmentWeight = Math.min(newMinEnrollmentWeight, e.getRequest().getWeight()); 700 } 701 } 702 iMinEnrollmentWeight = newMinEnrollmentWeight; 703 } 704 if (iMaxEnrollmentWeight == enrollment.getRequest().getWeight()) { 705 double newMaxEnrollmentWeight = Double.MIN_VALUE; 706 for (Enrollment e : iEnrollments) { 707 if (e.getRequest().getWeight() == iMaxEnrollmentWeight) { 708 newMaxEnrollmentWeight = iMaxEnrollmentWeight; 709 break; 710 } else { 711 newMaxEnrollmentWeight = Math.max(newMaxEnrollmentWeight, e.getRequest().getWeight()); 712 } 713 } 714 iMaxEnrollmentWeight = newMaxEnrollmentWeight; 715 } 716 } 717 } 718 719 /** Set of assigned enrollments 720 * @return assigned enrollments of this section 721 **/ 722 public Set<Enrollment> getEnrollments() { 723 return iEnrollments; 724 } 725 726 /** 727 * Enrollment weight -- weight of all requests which have an enrollment that 728 * contains this section, excluding the given one. See 729 * {@link Request#getWeight()}. 730 * @param assignment current assignment 731 * @param excludeRequest course request to ignore, if any 732 * @return enrollment weight 733 */ 734 public double getEnrollmentWeight(Assignment<Request, Enrollment> assignment, Request excludeRequest) { 735 double weight = iEnrollmentWeight; 736 if (excludeRequest != null && assignment.getValue(excludeRequest) != null && iEnrollments.contains(assignment.getValue(excludeRequest))) 737 weight -= excludeRequest.getWeight(); 738 return weight; 739 } 740 741 /** 742 * Maximal weight of a single enrollment in the section 743 * @return maximal enrollment weight 744 */ 745 public double getMaxEnrollmentWeight() { 746 return iMaxEnrollmentWeight; 747 } 748 749 /** 750 * Minimal weight of a single enrollment in the section 751 * @return minimal enrollment weight 752 */ 753 public double getMinEnrollmentWeight() { 754 return iMinEnrollmentWeight; 755 } 756 } 757}