The actual scheduler: Scheduler.oz

Source File

functor 
 
import 
 
   Open
 
   FD
 
   OS
 
   Search
 
   Compiler
 
   Inspector(inspect: Inspect)
 
   Graphics(drawSchedule: DrawSchedule)
 
   Controller(topWindow: TopWindow)
   Latex(laTeX: LaTeX)
   Main(controllerLabel: ControllerLabel)
 
   Common(editor:          Editor
          monday:          Monday
          tuesday:         Tuesday
          wednesday:       Wednesday
          thursday:        Thursday
          friday:          Friday
          quartersPerDay:  QuartersPerDay
          quartersPerHour: QuartersPerHour)
 
export 
 
   TimeTable
    
define 
    
   HourLimit           = 180
   LecturesStartForDay = 8
   MaximumRooms        = 7
   Teacher1Off         = 3
    
   Week = [Monday Tuesday Wednesday Thursday Friday]  
    
   MorningQuarters = 18
    
   [MondayM TuesdayM WednesdayM ThursdayM FridayM]
   = {Map Week fun {$ S#_} S#(S + MorningQuarters - 1) end}
 
   Afternoon  
   [MondayA TuesdayA WednesdayA ThursdayA FridayA]
   = Afternoon
   = {Map Week fun {$ S#E} (S + MorningQuarters)#end}
 
 
   fun {DayTimeToQuarters Day FH FQ TH TQ}
      Left Right Offset in 
      Offset = (36 * (Day.div QuartersPerDay) + 1)  
      Left  = Offset + (FH - LecturesStartForDay) * QuartersPerHour + FQ - 1
      Right = Offset + (TH-LecturesStartForDay) * QuartersPerHour + TQ - 1
      Left#Right
   end 
 
   AstaTime = {DayTimeToQuarters Tuesday 12 3 13 3}
 
 
   %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %% Lecture and Professor Manipulation :-} Functions
   %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
   local 
    
      DayMap= map(monday:    Monday
                  tuesday:   Tuesday
                  wednesday: Wednesday
                  thursday:  Thursday
                  friday:    Friday)
    
      %% Translate the constraint description
      %% into a domain that can stand after /* :: and \:: */
      fun {MakeC Desc}
         case Desc
         of inDays(L) then {Map L fun {$ D} DayMap.end}
         [] weekInterval(SH#SM#EH#EM) then 
            {Map Week
             fun {$ Day}
                {DayTimeToQuarters Day SH SM div 15 EH EM div 15}
             end}
         [] dayInterval(Day#SH#SM#EH#EM) then 
            [{DayTimeToQuarters DayMap.Day SH (SM div 15) EH (EM div 15)}]
         [] fix(Day#H#M) then 
            [{DayTimeToQuarters DayMap.Day H (M div 15) H (M div 15)}]
         else {Record.foldR Desc
               fun {$ D In} {Append {MakeC D} In} end nil}
         end 
      end 
 
      %% compute the constraint procedure
      %% for a lecturer from his/her constraint description
      proc {ApplyConstraint Desc X}
         case {Label Desc}
         of noT  then  X :: compl({MakeC Desc.1})
         [] nil         then  skip 
         else          X :: {MakeC Desc}
         end 
      end 
 
      %% compute the lectures of a prof
      %% from the lecture list
      fun {ProfToLectures PName Ls}
         {Filter Ls fun {$ L} L.professor==PName end}
      end 
 
   in 
       
      fun {MakeLectures LecturesDescs Semester Ordering}
         {Map {Record.toList LecturesDescs.Semester}
          fun {$ Ls}
             {Map Ls
              fun {$ L}
                 Start = {FD.int 1#(HourLimit-L.dur)}
              in 
                 if {HasFeature L constraints}
                 then {ApplyConstraint L.constraints Start}
                 else skip 
                 end 
                 {Adjoin L l(start:    Start
                             ordering: Ordering
                             semester: Semester)}
              end}
          end}
      end 
       
      %% translate the prof record into a
      %% list of records, containing constraint procedures
      fun {MakeProfessors ProfDesc Ls}
         {Record.foldRInd ProfDesc
          fun {$ I X In}
             PLs={ProfToLectures I Ls}
          in 
             {ForAll PLs proc {$ PL} {ApplyConstraint X PL.start} end}
             professor(lectures:PLs name:I) | In
          end 
          nil}
      end 
   end 
 
   %% get Lectures with size out of a given list of Specifiers
   fun {FilterSize Specifiers Lectures}
      {Filter Lectures fun {$ X} {Member X.size Specifiers} end}
   end 
 
   %% make record for all lectures for easy access
   fun {MakeLectureRecord Flats}
      {List.toRecord lectures {Map Flats fun {$ L} L.name#end}}
   end 
    
   %% search for professor in list of profs
   fun {SearchProf Profs Name}
      {List.dropWhile Profs fun {$ P} P.name \= Name end}.1
   end 
    
   %% %%%%%%%%%%%
   %% Enumeration
   %% %%%%%%%%%%%
 
   local 
      fun {SkipWait Xs}
         case Xs
         of X|Xr then case {FD.reflect.size X.start}
                      of 1 then {SkipWait Xr}
                      else Xs
                      end 
         [] nil then nil
         end 
      end 
       
      proc {Choose Xs HoleTail HoleHead MinYet SizeYet ?Min ?Ys}
         case Xs
         of X|Xr then  
            SizeX = {FD.reflect.size X.start} in 
            case SizeX of 1 then  
               {Choose Xr HoleTail HoleHead MinYet SizeYet Min Ys}
            elseif SizeX < SizeYet then 
               NewHole in 
               HoleTail = MinYet | HoleHead
               {Choose Xr Ys NewHole X SizeX Min NewHole}
            else 
               Ys=X | {Choose Xr HoleTail HoleHead MinYet SizeYet Min}
            end 
         [] nil then Min = MinYet Ys = nil HoleTail = HoleHead
         end 
      end 
      fun {Cost I Ls C}
         case Ls of nil then 
            {Exception.raiseError college(Cost [I Ls C] 'tragic error')}
            _
         [] A#B|Lr then 
            if A =< I andthen I =< B
            then C
            else {Cost I Lr C+1}
            end 
         end 
      end 
 
      fun {GetFirst Domain Min MinVal Ordering}
         case Domain of nil then Min
         [] D|Dr
         then TMinVal = {Cost D Ordering 1} in 
            if TMinVal<MinVal then {GetFirst Dr D TMinVal Ordering}
            else {GetFirst Dr Min MinVal Ordering}
            end 
         end 
      end 
   in 
      proc {Enum Xs}
         choice 
            case {SkipWait Xs}
            of nil then skip 
            [] X|Xr then 
               Y Yr Hole Val YDom YVar in 
               {Choose Xr Yr Hole X {FD.reflect.size X.start} Y Hole}
               {Wait Y}
               YVar = Y.start
               YDom = {FD.reflect.domList YVar}
               Val = {GetFirst YDom.2 YDom.1 {Cost YDom.1 Y.ordering 1}
                      Y.ordering}
               choice 
                  YVar=Val {Enum Yr}
               [] 
                  YVar\=:Val {Enum Y|Yr}
               end 
            end 
         end 
      end 
   end 
 
   %% %%%%%%%%%%%%%%%%%%%%%
   %% Branch and Bound Cost Function
   %% %%%%%%%%%%%%%%%%%%%%%
   local  
      fun {SumUp Lectures Time}
         case Lectures of nil then nil
         [] L|Lr
         then case {Atom.toString L.name}.1
              of &M then 0|{SumUp Lr Time}
              [] &F then 0|{SumUp Lr Time}
              else (L.start::Time)|{SumUp Lr Time}
              end 
         end 
      end 
   in 
      fun {Cost Lectures}
         Costs = {FD.decl} in 
         {FD.sum {SumUp Lectures [MondayA TuesdayA WednesdayA ThursdayA FridayA]} '=:' Costs}
         Costs
      end 
   end 
    
 
   %% %%%%%%%%%%%%%%%%%%%%%
   %% Constraint Procedures
   %% %%%%%%%%%%%%%%%%%%%%%
 
   proc {OnSpecialDayOnly Lectures Day}
      {ForAll Lectures proc {$ Lec} Lec.start :: Day end}
   end 
   proc {OnSpecialTimeOnly Lectures Time}
      {ForAll Lectures proc {$ Lec} Lec.start :: Time end}
   end 
   proc {ForbidAsta Lectures}
      {ForAll Lectures
       proc {$ Lec} Lec.start :: compl((AstaTime.1-Lec.dur+1)#AstaTime.2) end}
   end 
   proc {DayBreak Break Lectures}
      {ForAll Lectures
       proc {$ Lec}
          {ForAll Break proc{$ B}
                           Left = (B.1-Lec.dur)
                        in 
                           if Left < 0 then 
                              Lec.start :: compl(0#B.2)
                           else 
                              Lec.start :: compl(Left#B.2)
                           end 
                        end}
       end}
   end 
    
   proc {NotParallel Offset Teacher1 Teacher15}
      {ForAll Teacher1.lectures
       proc {$ FL}
          {ForAll Teacher15.lectures
           proc{$ LFL}
              {FD.disjoint FL.start FL.dur+Offset LFL.start LFL.dur+Offset}
           end}
       end}
   end 
 
   %% The lectures of a semester must not overlap
    
   proc {NoOverlapSemester Lectures}
      case Lectures of nil then skip 
      [] L|Lr
      then {NoOverlap1 Lr L} {NoOverlapSemester Lr}
      end 
   end 
   proc {NoOverlap1 Lecs Lec1}
      case Lecs of nil then skip 
      [] L|Lr
      then {NoOverlap2 Lec1 L} {NoOverlap1 Lr Lec1}
      end 
   end 
   proc {NoOverlap2 L1 L2}
      {ForAll L1
       proc{$ L}
          {ForAll L2
           proc {$ LP}
              %% After one hour must be a quarter
              %% and after two hours two quarters
              %% recreation time
              LDur LPDur in 
              LDur= if L.dur<4
                    then L.dur+1
                    else L.dur+2
                    end 
              LPDur= if LP.dur<4
                     then LP.dur+1
                     else LP.dur+2
                     end 
              {FD.disjoint L.start LDur LP.start LPDur}
           end}
       end}
   end 
 
   %% The lectures of a professor must not overlap
   proc {NoOverlapLectures Lectures}
      case Lectures of nil then skip 
      [] L|Lr
      then {NoOverlapLecs1 L Lr} {NoOverlapLectures Lr}
      end 
   end 
 
   proc {NoOverlapLecs1 Lec Lec1}
      case Lec1 of nil then skip 
      [] L|Lr
      then {NoOverlapLecs2 Lec L} {NoOverlapLecs1 Lec Lr}
      end 
   end 
    
   proc {NoOverlapLecs2 L1 L2}
      %% After one hour must be a quarter
      %% and after two hours two quarters
      %% recreation time
      L1Dur L2Dur in 
      L1Dur= if L1.dur < 4 then L1.dur + 1 else L1.dur + 2 end 
      L2Dur= if L2.dur < 4 then L2.dur + 1 else L2.dur + 2
             end 
      {FD.disjoint L1.start L1Dur L2.start L2Dur}
   end 
 
   %% Constraint AtMostLectures for rooms
    
   local  
      fun {SumUpLectures Lectures Hour}
         case Lectures
         of L|Lr
         then 
            %% Rooms are empty for a quarter after each lecture
            Left = Hour-(L.dur+1)+1
         in  
            if Left < 0 then (L.start :: 0#Hour)|{SumUpLectures Lr Hour}
            else             (L.start :: Left#Hour)|{SumUpLectures Lr Hour}
            end 
         [] nil then nil
         end 
      end 
   in 
      proc {AtMostLectures Lectures Limit}
         {For 1 HourLimit 1
          proc{$ Hour}
             {FD.sum {SumUpLectures Lectures Hour} '=<:' Limit}
          end}
      end 
   end 
 
   %% Constraint ThreeDaysOnly and OnDifferentDays
 
   local 
      fun {SumUp Lectures Day}
         case Lectures of nil then nil
         [] L|Lr
         then (L.start :: Day)|{SumUp Lr Day}
         end 
      end 
      proc {SumUpDays Lectures BDay Day}
         S= {SumUp Lectures Day}
      in 
         BDay = ({FoldL S FD.plus 0} >=: 1)
      end 
   in 
      proc {ThreeDaysOnly Lectures DayLimit}
         [BMo BTu BWe BTh BFr]={FD.dom 0#1}
      in 
         BMo+BTu+BWe+BTh+BFr =<: DayLimit
         {SumUpDays Lectures BMo Monday}
         {SumUpDays Lectures BTu Tuesday}
         {SumUpDays Lectures BWe Wednesday}
         {SumUpDays Lectures BTh Thursday}
         {SumUpDays Lectures BFr Friday}
      end 
      proc {OnDifferentDays Lectures}
         [BMo BTu BWe BTh BFr]={FD.dom 0#1}
      in 
         BMo+BTu+BWe+BTh+BFr =: {Length Lectures}
         {SumUpDays Lectures BMo Monday}
         {SumUpDays Lectures BTu Tuesday}
         {SumUpDays Lectures BWe Wednesday}
         {SumUpDays Lectures BTh Thursday}
         {SumUpDays Lectures BFr Friday}
      end 
      proc {OnSameDay Lectures}
         [BMo BTu BWe BTh BFr]={FD.dom 0#1}
      in 
         BMo+BTu+BWe+BTh+BFr =: 1
         {SumUpDays Lectures BMo Monday}
         {SumUpDays Lectures BTu Tuesday}
         {SumUpDays Lectures BWe Wednesday}
         {SumUpDays Lectures BTh Thursday}
         {SumUpDays Lectures BFr Friday}
      end          
   end 
   local 
      fun {SumUpOverlaps L1 Lectures}
         case Lectures of nil then nil
         [] L2|Lr then B={FD.int 0#1} L1Dur L2Dur in 
            if L1.name==L2.name then 0|{SumUpOverlaps L1 Lr}
            else L1S L2S in 
               L1Dur= if L1.dur < 4 then L1.dur + 1 else L1.dur + 2 end 
               L2Dur= if L2.dur < 4 then L2.dur + 1 else L2.dur + 2 end 
               L1S = L1.start
               L2S = L2.start
 
               /* That is the (operational) semantics of FD.tasksOverlap.
                  FD.tasksOverlap implements constructive disjunction.
               condis
                  B=:1
                  L1S + L1Dur >: L2S
                  L2S + L2Dur >: L1S
               []
                  B=:0
                  L1S + L1Dur =<: L2S
               []
                  B=:0
                  L2S + L2Dur =<: L1S
               end
               */
 
               {FD.tasksOverlap L1S L1Dur L2S L2Dur B}
               B|{SumUpOverlaps L1 Lr}
            end 
         end 
      end  
   in 
      proc {AtmostOneOverlap Lecture Lectures}
         {FD.sum {SumUpOverlaps Lecture Lectures} '=<:' 1}
      end 
   end 
 
  
   %% %%%%%%%%%%%
   %% The Problem
   %% %%%%%%%%%%%
 
   %% MakeProblem makes the problem procedure
   %% using the problem description as global variable
    
   fun {MakeProblem ProblemDescription}
 
      proc {$ FlatAllLectures}
          
         %% %%%%%%%%
         %% Lectures
         %% %%%%%%%%
          
         LecturesDescription = ProblemDescription.lectures
 
         %% Second Semester
         %% %%%%%%%%%%%%%%%
          
         LecturesSecond = {MakeLectures
                           LecturesDescription
                           second
                           %% other priorities
 
                           [MondayM MondayA
                            TuesdayA TuesdayM
                            WednesdayM WednesdayA
                            FridayM  FridayA
                            ThursdayM ThursdayA
                           ]
                          }
         %% Fourth Semester
         %% %%%%%%%%%%%%%%%
 
         LecturesFourth = {MakeLectures
                           LecturesDescription
                           fourth
                           %% other priorities
 
                           [
                            WednesdayM WednesdayA
                            TuesdayM TuesdayA
                            MondayM MondayA
                            ThursdayM ThursdayA
                            FridayM FridayA
                           ]
 
 
                          }
         GrundStudiumLectures = {Append LecturesSecond LecturesFourth}
         FlatGrundStudiumLectures = {List.flatten GrundStudiumLectures}
 
         %% Sixth Semester
         %% %%%%%%%%%%%%%%
 
         LecturesSixth = {MakeLectures
                          LecturesDescription
                          sixth
                          %% other priorities
 
                          [ WednesdayM WednesdayA
                            FridayM FridayA
                            ThursdayM ThursdayA
                            MondayM MondayA
                            TuesdayM TuesdayA
                          ]
                         }
 
         FlatLecturesSixth = {List.flatten LecturesSixth}
 
         %% Eighth Semester
         %% %%%%%%%%%%%%%%%
 
         LecturesEighth = {MakeLectures
                           LecturesDescription
                           eighth
                           [
                            ThursdayM ThursdayA
                            WednesdayA WednesdayM
                            MondayA  MondayM
                            TuesdayA TuesdayM
                            FridayM FridayA
                           ]}
          
         FlatLecturesEighth = {List.flatten LecturesEighth}
 
         NotOnThursdayLectures = {Append GrundStudiumLectures LecturesSixth}
         AllSemesterLectures = {Append NotOnThursdayLectures LecturesEighth}
         FlatAllSemesterLectures={List.flatten AllSemesterLectures}
 
         %% Medien Lectures
         %% %%%%%%%%%%%%%%%
 
         MedienLectures = {MakeLectures
                           LecturesDescription
                           medien
                           [
                            ThursdayA TuesdayA
                            TuesdayM ThursdayM
                            WednesdayM WednesdayA
                            MondayM MondayA
                            FridayM FridayA
                           ]}
 
         FlatMedienLectures = {List.flatten MedienLectures}
          
         %% Fakult Lectures
         %% %%%%%%%%%%%%%%%
 
         FakLectures = {MakeLectures
                        LecturesDescription
                        fac
                        [
                         WednesdayM WednesdayA
                         ThursdayM ThursdayA
                         MondayM MondayA
                         FridayM FridayA
                         TuesdayM TuesdayA
                        ]
                       }
 
         FlatFakLectures = {List.flatten FakLectures}
 
         AllLectures = {Append AllSemesterLectures
                        {Append MedienLectures FakLectures}}
 
         !FlatAllLectures = {List.flatten AllLectures}
 
         L = {MakeLectureRecord FlatAllLectures}
          
         %% %%%%%%%%%%
         %% Professors
         %% %%%%%%%%%%
 
         ProfessorsDescription = ProblemDescription.professors
 
         Professors = {MakeProfessors ProfessorsDescription FlatAllLectures}
 
      in 
 
         %% %%%%%%%%%%%%%%%
         %% The Constraints
         %% %%%%%%%%%%%%%%%
 
         %% General constraints
         %% Lecture must be finished at 17.00
 
         {ForAll FlatAllLectures
          proc{$ L}
             Dur = L.dur
             fun {AfterHours Day} Day.+ 1 - Dur#Day.2  end 
          in 
             L.start :: compl({Map Week AfterHours})
          end}
 
 
         %% All semesters but the eighth are not scheduled on thursday
         %% seems to be to hard
          
         {OnSpecialDayOnly FlatLecturesSixth Wednesday}
         %% The break must be lecturefree
 
         {DayBreak
          {List.map [0 1 2 3 4] fun{$ I} (20+I*36)#(23+I*36) end}
          {Append FlatLecturesEighth FlatGrundStudiumLectures}}
          
         {DayBreak {List.map [0 1 2 3 4]
                    fun{$ I} (16+I*36)#(19+I*36) end}
          FlatLecturesSixth}
 
         %% At Asta time no lectures allowed
         {ForbidAsta FlatAllLectures}
 
         %% The Lectures for a semester must not overlap
         {NoOverlapSemester [[L.'2.1' L.'2.2' L.'2.3']
                             [L.'2.6']
                             [L.'2.7']
                             [ L.'2.9']
                             [L.'2.10.1' L.'2.10.2' L.'2.10.3']
                             [L.'2.5.1' L.'2.5.2' L.'2.8.1' L.'2.8.2' 
                              L.'2.13.1' L.'2.13.2' L.'2.4.1' L.'2.4.2' 
                              L.'2.14.1' L.'2.14.2' L.'2.15.1' L.'2.15.2' 
                              L.'2.15.3' L.'2.15.4' ]]}
 
         {ForAll [L.'2.5.1' L.'2.5.2' L.'2.8.1' L.'2.8.2' 
                  L.'2.13.1' L.'2.13.2' L.'2.4.1' L.'2.4.2' 
                  L.'2.14.1' L.'2.14.2']
          proc{$ Lecture}
             {AtmostOneOverlap Lecture [L.'2.5.1' L.'2.5.2' L.'2.8.1' 
                                        L.'2.8.2' L.'2.13.1' L.'2.13.2' 
                                        L.'2.4.1' L.'2.4.2' L.'2.14.1' 
                                        L.'2.14.2' L.'2.15.1' L.'2.15.2' 
                                        L.'2.15.3' L.'2.15.4']}
          end}
         {ForAll [L.'2.15.1' L.'2.15.2' L.'2.15.3' L.'2.15.4']
          proc{$ Lecture}
             {AtmostOneOverlap Lecture [L.'2.5.1' L.'2.5.2' L.'2.8.1' 
                                        L.'2.8.2' L.'2.13.1' L.'2.13.2' 
                                        L.'2.4.1' L.'2.4.2' L.'2.14.1' 
                                        L.'2.14.2']}
          end}
 
 
         {NoOverlapSemester [[L.'4.1'] [L.'4.6'] [L.'4.11' ]
                             [ L.'4.2.1' L.'4.2.2' L.'4.3.1' L.'4.3.2' 
                               L.'4.4.1' L.'4.4.2' L.'4.5.1' L.'4.5.2'  
                               L.'4.7.1' L.'4.7.2' L.'4.10.1' L.'4.10.2'  
                               L.'4.12.1' L.'4.12.2' 
                               L.'4.8.1' L.'4.8.2' L.'4.8.3' L.'4.8.4']]}
 
         {ForAll [ L.'4.2.1' L.'4.2.2' L.'4.3.1' L.'4.3.2' 
                   L.'4.4.1' L.'4.4.2' L.'4.5.1' L.'4.5.2'  
                   L.'4.7.1' L.'4.7.2' L.'4.10.1' L.'4.10.2' 
                   L.'4.12.1' L.'4.12.2']
          proc{$ Lecture}
             {AtmostOneOverlap
              Lecture [ L.'4.2.1' L.'4.2.2' L.'4.3.1' L.'4.3.2' 
                        L.'4.4.1' L.'4.4.2' L.'4.5.1' L.'4.5.2'  
                        L.'4.7.1' L.'4.7.2' L.'4.10.1' L.'4.10.2' 
                        L.'4.12.1' L.'4.12.2' 
                        L.'4.8.1' L.'4.8.2' L.'4.8.3' L.'4.8.4']}
          end}
 
         {ForAll [L.'4.8.1' L.'4.8.2' L.'4.8.3' L.'4.8.4']
          proc{$ Lecture}
             {AtmostOneOverlap
              Lecture [ L.'4.2.1' L.'4.2.2' L.'4.3.1' L.'4.3.2' 
                        L.'4.4.1' L.'4.4.2' L.'4.5.1' L.'4.5.2'  
                        L.'4.7.1' L.'4.7.2' L.'4.10.1' L.'4.10.2' 
                        L.'4.12.1' L.'4.12.2' ]}
          end}
 
 
 
         {NoOverlapSemester [[L.'6.2.1' L.'6.2.2'] [L.'6.5' L.'6.6'] ]}
         {NoOverlapSemester [[L.'8.1'][L.'8.2'][L.'8.3'][L.'8.4']
                             [L.'8.5'][L.'8.6'][L.'8.7.2'][L.'8.8']
                             [L.'8.9'][L.'8.10']
                            ]}
 
         %% Lectures for a professor must not overlap
         {ForAll Professors proc{$ L} {NoOverlapLectures L.lectures} end}
 
         %% At most MaximumRooms concurrent
         {AtMostLectures {FilterSize [big small tiny] FlatAllLectures}
          MaximumRooms}
 
         %% Aula and 155 lectures at most 2
         {AtMostLectures {FilterSize [big] FlatAllLectures} 2}
 
         %% Rooms Aula, 155, 152, 154, 250 lectures at most 5
         {AtMostLectures {FilterSize [big small] FlatAllLectures} 5}
 
         %% All professors have a teaching limit of 3 days per week
         {ForAll Professors
          proc{$ Professor} {ThreeDaysOnly Professor.lectures 3} end}
 
         %% At most 6 hours per day
         {ForAll Professors
          proc{$ Professor}
             {ForAll [Monday Tuesday Wednesday Thursday Friday]
              proc{$ Day}
                 {FoldL Professor.lectures
                  fun{$ I L}
                     {FD.plus {FD.times L.dur (L.start::Day)} I}
                  end 0} =<: 6*4
              end}
          end}
          
         %% Teacher1/Teacher15 couple cannot teach at the same time (+offset)
         {NotParallel
          Teacher1Off
          {SearchProf Professors 'Teacher1'}
          {SearchProf Professors 'Teacher15'}}
          
         %% Special constraints for 2nd semester
         L.'2.1'.start=L.'2.2'.start=L.'2.3'.start
         L.'2.10.1'.start=L.'2.10.2'.start=L.'2.10.3'.start
         L.'2.15.1'.start=L.'2.15.2'.start=L.'2.15.3'.start=L.'2.15.4'.start  
 
         %% Special constraints for 4th semester
         {OnDifferentDays [L.'4.7.1' L.'4.7.2']}  % Teacher17's lecture
          
         %% Special constraints for 6th semester
         L.'6.5'.start=L.'6.6'.start
         (L.'6.5'.start) >: {FD.max L.'6.2.1'.start L.'6.2.2'.start}
 
         L.'6.2.1'.start=L.'6.2.2'.start=L.'6.2.3'.start
 
         %% Special constraints for eighth semester
 
         %% Special constraints for Mediendidaktik
         {NoOverlapSemester [FlatGrundStudiumLectures FlatMedienLectures]}
 
         %% Special constraints for Fakultative Veranstaltungen
         {NoOverlapSemester FakLectures}
         {OnSpecialTimeOnly FlatFakLectures Afternoon}
 
         %% Special constraint for Teacher32
         {OnSameDay [L.'F.2.1' L.'F.2.2']}
          
         %% %%%%%%%%%%%%%%%
         %% The Enumeration
         %% %%%%%%%%%%%%%%%
 
         {Enum FlatAllSemesterLectures}
         {Enum {Append FlatMedienLectures FlatFakLectures}}
      end 
   end       
 
   class TimeTableClass from BaseObject 
      attr 
         problemDescription: nil
         solution
         dir
         first: true 
         solver
 
      meth readProblem(InputFileName)
         if InputFileName==false then skip 
         else 
            InputFile = {New Open.file init(name:InputFileName
                                            flags: [read])}
            Read = {InputFile read(size:all list:$)}
         in 
            problemDescription <- {Compiler.virtualStringToValue Read}
         end 
      end 
 
      meth setDir(D)
         dir <- D
      end 
 
      meth getDir(?D)
         D = @dir  
      end 
 
      meth setProblem(P)
         problemDescription <- P
      end 
 
      meth inspectProblem 
         {Inspect @problemDescription}
      end 
 
      meth constrainProblem 
         solution <- {{MakeProblem @problemDescription}}
      end 
 
      meth solveProblem 
         PD = @problemDescription
      in 
         first <- true 
         if PD == nil then 
            {ControllerLabel tk(configure text:'No problem loaded')}
         else 
            solver <- {New Search.object
                       script({MakeProblem PD}
                              proc{$ Old New}
                                 CN = {Cost New}
                                 CO = {Cost Old} in 
                                 CN <: CO
                                 thread 
                                    {Wait CO}
                                    /*{Show {FD.reflect.min CO}}*/ 
                                 end 
                              end 
                              rcd : 20)}
            local Solver = @solver in  
               {self setSolution(thread {Solver next($)} end)}
            end 
         end 
      end 
      meth optimizeProblem 
         Solver = @solver in 
         {ControllerLabel tk(configure text:'Computing...')}
         {self setSolution(thread {Solver next($)} end)}
      end 
      meth setSolution(Sol)
         thread 
            if Sol==nil then 
               {ControllerLabel tk(configure text:'No solution')}
            elseif Sol==stopped then 
               {ControllerLabel tk(configure text:'Stopped')}
            else 
               {self help(Sol)}
            end 
         end 
      end 
      meth help(Sol)
         Solver = @solver in  
         solution <- Sol
         if @first  then 
            first <- false 
            {self graphic}
         else skip 
         end 
         {self setSolution(thread {Solver next($)} end)}
      end 
      meth get(?Sol)
         Sol=@solution
      end 
      meth anyTime 
         {@solver stop}
      end 
      meth inspect 
         {Inspect {Map @solution fun {$ X} {Record.subtract X ordering} end}}
      end 
      meth text 
         {LaTeX show @solution}
      end 
      meth print 
         {LaTeX print @solution}
      end 
      meth graphic 
         {DrawSchedule @solution.1 TopWindow}
      end 
      meth edit(FileName)
         if FileName==false then skip 
         else  
            {OS.system Editor#' '#FileName#'\&' _}
         end 
      end 
      meth save(OutputFileName)
         if OutputFileName==false then skip 
         else  
            F = {New Open.file init(name:OutputFileName
                                    flags: [read write 'create'])}
         in 
            {F write(vs:{Value.toVirtualString @solution 100 100})}
         end 
      end 
\ifndef ALONEDEMO 
      meth read(FileName)
         if FileName==false then skip 
         else  
            File = {New Open.file init(name:FileName
                                       flags: [read write 'create'])}
         in 
            solution <- {Compiler.virtualStringToValue
                         {File read(size:all list:$)}}
         end 
      end 
\else 
      meth read(_)
         solution <-  
         \insert KathHochWS9596Sol.ozt 
      end 
\endif 
   end 
    
   TimeTable = {New TimeTableClass         
\ifdef ALONEDEMO 
                setProblem(
                   \insert KathHochWS9596.ozt 
                   )
\else 
                noop
\endif 
               }
end 


Version 1.4.0 (20080702)