The actual scheduler: Scheduler.oz

### 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)#E end}

>        fun {DayTimeToQuarters Day FH FQ TH TQ}
>           Left Right Offset in
>           Offset = (36 * (Day.1 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.D 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#L 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.2 + 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