{-# LANGUAGE CPP, FlexibleContexts, UndecidableInstances #-}
{-
	Copyright (C) 2013-2015 Dr. Alistair Ward

	This file is part of WeekDaze.

	WeekDaze is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	WeekDaze 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with WeekDaze.  If not, see <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@]

	* Defines the weightings, associated with each of the /soft/-constraints used to compare & select between alternative /timetable/s.

	* /Hard/-constraints (e.g. that a /student/ or /teacher/ can't be in more than one place at a /time/,
	or that the capacity of a /location/ can't be exceeded),
	are never violated, & so don't need to be quantified for subsequent comparison.
-}

module WeekDaze.ExecutionConfiguration.TimetableCriteriaWeights(
-- * Types
-- ** Type-synonyms
--	Mutator,
-- ** Data-types
        TimetableCriteriaWeights(..),
-- * Constants
        associationList,
        tag,
        weightOfMaximiseComplianceWithFreePeriodPreferencesTag,
        weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacityTag,
        weightOfMaximiseMeanStudentClassSizeTag,
        weightOfMaximiseSynchronisationOfSynchronisedCoursesTag,
        weightOfMaximiseWeightedMeanStudentBodyUtilisationRatioTag,
        weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag,
        weightOfMinimiseDispersionOfStudentFreePeriodsPerDayTag,
        weightOfMinimiseDispersionOfTeacherFreePeriodsPerDayTag,
        weightOfMinimiseDispersionOfTeacherWorkloadTag,
        weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag,
        weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag,
        weightOfMinimiseMeanLocationChangesOfTeachersTag,
        weightOfMinimiseMeanLocusOperandiOfTeachersTag,
        weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledgeTag,
        weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag,
        weightOfMinimiseMeanStudentBodyCombinationsPerLessonTag,
        weightOfMinimiseRatioOfConsecutiveEqualLessonsTag,
        weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDayTag,
-- * Functions
        calculateWeightedMean,
-- ** Mutators
        zeroWeightOfMaximiseComplianceWithFreePeriodPreferences,
--	zeroWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity,
--	zeroWeightOfMaximiseMeanStudentClassSize,
        zeroWeightOfMaximiseSynchronisationOfSynchronisedCourses,
--	zeroWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio,
        zeroWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest,
        zeroWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay,
--	zeroWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay,
--	zeroWeightOfMinimiseDispersionOfTeacherWorkload,
        zeroWeightOfMinimiseMeanInterCampusMigrationsOfStudents,
        zeroWeightOfMinimiseMeanInterCampusMigrationsOfTeachers,
        zeroWeightOfMinimiseMeanLocationChangesOfTeachers,
        zeroWeightOfMinimiseMeanLocusOperandiOfTeachers,
--	zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge,
        zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge,
        zeroWeightOfMinimiseMeanStudentBodyCombinationsPerLesson,
        zeroWeightOfMinimiseRatioOfConsecutiveEqualLessons,
        zeroWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay
) where

import qualified        Control.DeepSeq
import qualified        Control.Monad.Writer
import qualified        Data.Default
import qualified        Text.XML.HXT.Arrow.Pickle                       as HXT
import qualified        ToolShed.SelfValidate
import qualified        WeekDaze.ExecutionConfiguration.Criterion       as ExecutionConfiguration.Criterion
import qualified        WeekDaze.ExecutionConfiguration.CriterionWeight as ExecutionConfiguration.CriterionWeight

#ifdef USE_HDBC
import qualified        Database.HDBC
import qualified        Data.Convertible
import qualified        Data.Maybe
import qualified        Data.Typeable
import qualified        WeekDaze.Database.Selector                      as Database.Selector

instance (
        Data.Convertible.Convertible    Database.HDBC.SqlValue w,       -- Flexible context & Undecidable instance.
        Data.Typeable.Typeable          w,
        RealFrac                        w
 ) => Database.Selector.Selector (TimetableCriteriaWeights w) where
        fromDatabase connection projectIdSql    = let
                tableName :: Database.Selector.TableName
                tableName       = Database.Selector.tablePrefix ++ tag
         in do
                criterionWeightRows     <- map (
                        map $ Data.Maybe.fromMaybe Data.Default.def . either (
                                error . showString "WeekDaze.ExecutionConfiguration.TimetableCriteriaWeights.fromDatabase:\tfailed to parse the value of a weight read from the database; " . show
                        ) id . Database.HDBC.safeFromSql
                 ) `fmap` Database.Selector.select connection (
                        map fst associationList
                 ) [tableName] [(Database.Selector.projectIdColumnName, projectIdSql)]

                return {-to IO-Monad-} $ case criterionWeightRows of
                        []              -> Data.Default.def
                        [weightRow]     -> case weightRow of
                                [
                                        weightOfMaximiseComplianceWithFreePeriodPreferences,
                                        weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity,
                                        weightOfMaximiseMeanStudentClassSize,
                                        weightOfMaximiseSynchronisationOfSynchronisedCourses,
                                        weightOfMaximiseWeightedMeanStudentBodyUtilisationRatio,
                                        weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest,
                                        weightOfMinimiseDispersionOfStudentFreePeriodsPerDay,
                                        weightOfMinimiseDispersionOfTeacherFreePeriodsPerDay,
                                        weightOfMinimiseDispersionOfTeacherWorkload,
                                        weightOfMinimiseMeanInterCampusMigrationsOfStudents,
                                        weightOfMinimiseMeanInterCampusMigrationsOfTeachers,
                                        weightOfMinimiseMeanLocationChangesOfTeachers,
                                        weightOfMinimiseMeanLocusOperandiOfTeachers,
                                        weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge,
                                        weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge,
                                        weightOfMinimiseMeanStudentBodyCombinationsPerLesson,
                                        weightOfMinimiseRatioOfConsecutiveEqualLessons,
                                        weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay
                                 ] -> MkTimetableCriteriaWeights {
                                        getWeightOfMaximiseComplianceWithFreePeriodPreferences                  = weightOfMaximiseComplianceWithFreePeriodPreferences,
                                        getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity        = weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity,
                                        getWeightOfMaximiseMeanStudentClassSize                                 = weightOfMaximiseMeanStudentClassSize,
                                        getWeightOfMaximiseSynchronisationOfSynchronisedCourses                 = weightOfMaximiseSynchronisationOfSynchronisedCourses,
                                        getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio              = weightOfMaximiseWeightedMeanStudentBodyUtilisationRatio,
                                        getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest     = weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest,
                                        getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay                 = weightOfMinimiseDispersionOfStudentFreePeriodsPerDay,
                                        getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay                 = weightOfMinimiseDispersionOfTeacherFreePeriodsPerDay,
                                        getWeightOfMinimiseDispersionOfTeacherWorkload                          = weightOfMinimiseDispersionOfTeacherWorkload,
                                        getWeightOfMinimiseMeanInterCampusMigrationsOfStudents                  = weightOfMinimiseMeanInterCampusMigrationsOfStudents,
                                        getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers                  = weightOfMinimiseMeanInterCampusMigrationsOfTeachers,
                                        getWeightOfMinimiseMeanLocationChangesOfTeachers                        = weightOfMinimiseMeanLocationChangesOfTeachers,
                                        getWeightOfMinimiseMeanLocusOperandiOfTeachers                          = weightOfMinimiseMeanLocusOperandiOfTeachers,
                                        getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge           = weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge,
                                        getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge       = weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge,
                                        getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson                 = weightOfMinimiseMeanStudentBodyCombinationsPerLesson,
                                        getWeightOfMinimiseRatioOfConsecutiveEqualLessons                       = weightOfMinimiseRatioOfConsecutiveEqualLessons,
                                        getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay             = weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay
                                }
                                _ -> error $ "WeekDaze.ExecutionConfiguration.TimetableCriteriaWeights.fromDatabase:\tunexpected number of columns=" ++ show (length weightRow) ++ " in row of table " ++ show tableName ++ "."
                        _               -> error $ "WeekDaze.ExecutionConfiguration.TimetableCriteriaWeights.fromDatabase:\tunexpected number of rows=" ++ show (length criterionWeightRows) ++ " selected from table " ++ show tableName ++ "."
#endif /* USE_HDBC */

-- | Used to qualify XML.
tag :: String
tag                                                                     = "timetableCriteriaWeights"

-- | Used to qualify XML.
weightOfMaximiseComplianceWithFreePeriodPreferencesTag :: String
weightOfMaximiseComplianceWithFreePeriodPreferencesTag                  = "maximiseComplianceWithFreePeriodPreferences"

-- | Used to qualify XML.
weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacityTag :: String
weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacityTag        = "maximiseMeanRatioOfStudentClassSizeToLocationCapacity"

-- | Used to qualify XML.
weightOfMaximiseMeanStudentClassSizeTag :: String
weightOfMaximiseMeanStudentClassSizeTag                                 = "maximiseMeanStudentClassSize"

-- | Used to qualify XML.
weightOfMaximiseSynchronisationOfSynchronisedCoursesTag :: String
weightOfMaximiseSynchronisationOfSynchronisedCoursesTag                 = "maximiseSynchronisationOfSynchronisedCourses"

-- | Used to qualify XML.
weightOfMaximiseWeightedMeanStudentBodyUtilisationRatioTag :: String
weightOfMaximiseWeightedMeanStudentBodyUtilisationRatioTag              = "maximiseWeightedMeanStudentBodyUtilisationRatio"

-- | Used to qualify XML.
weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag :: String
weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag     = "minimiseAverageAbsoluteDeviationFromIdealTimeslotRequest"

-- | Used to qualify XML.
weightOfMinimiseDispersionOfStudentFreePeriodsPerDayTag :: String
weightOfMinimiseDispersionOfStudentFreePeriodsPerDayTag                 = "minimiseDispersionOfStudentFreePeriodsPerDay"

-- | Used to qualify XML.
weightOfMinimiseDispersionOfTeacherFreePeriodsPerDayTag :: String
weightOfMinimiseDispersionOfTeacherFreePeriodsPerDayTag                 = "minimiseDispersionOfTeacherFreePeriodsPerDay"

-- | Used to qualify XML.
weightOfMinimiseDispersionOfTeacherWorkloadTag :: String
weightOfMinimiseDispersionOfTeacherWorkloadTag                          = "minimiseDispersionOfTeacherWorkload"

-- | Used to qualify XML.
weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag :: String
weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag                  = "minimiseMeanInterCampusMigrationsOfStudents"

-- | Used to qualify XML.
weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag :: String
weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag                  = "minimiseMeanInterCampusMigrationsOfTeachers"

-- | Used to qualify XML.
weightOfMinimiseMeanLocationChangesOfTeachersTag :: String
weightOfMinimiseMeanLocationChangesOfTeachersTag                        = "minimiseMeanLocationChangesOfTeachers"

-- | Used to qualify XML.
weightOfMinimiseMeanLocusOperandiOfTeachersTag :: String
weightOfMinimiseMeanLocusOperandiOfTeachersTag                          = "minimiseMeanLocusOperandiOfTeachers"

-- | Used to qualify XML.
weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledgeTag :: String
weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledgeTag           = "minimiseMeanRatioOfIncompletelyBookedCoreKnowledge"

-- | Used to qualify XML.
weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag :: String
weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag       = "minimiseMeanRatioOfIncompletelyBookedOptionalKnowledge"

-- | Used to qualify XML.
weightOfMinimiseMeanStudentBodyCombinationsPerLessonTag :: String
weightOfMinimiseMeanStudentBodyCombinationsPerLessonTag                 = "minimiseMeanStudentBodyCombinationsPerLesson"

-- | Used to qualify XML.
weightOfMinimiseRatioOfConsecutiveEqualLessonsTag :: String
weightOfMinimiseRatioOfConsecutiveEqualLessonsTag                       = "minimiseRatioOfConsecutiveEqualLessons"

-- | Used to qualify XML.
weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDayTag :: String
weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDayTag             = "minimiseRatioOfSeparatedEqualLessonsWithinAnyDay"

-- | The ordered list of tags & accessors.
associationList :: [(String, TimetableCriteriaWeights w -> ExecutionConfiguration.CriterionWeight.CriterionWeight w)]
associationList = [
        (weightOfMaximiseComplianceWithFreePeriodPreferencesTag,                getWeightOfMaximiseComplianceWithFreePeriodPreferences),
        (weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacityTag,      getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity),
        (weightOfMaximiseMeanStudentClassSizeTag,                               getWeightOfMaximiseMeanStudentClassSize),
        (weightOfMaximiseSynchronisationOfSynchronisedCoursesTag,               getWeightOfMaximiseSynchronisationOfSynchronisedCourses),
        (weightOfMaximiseWeightedMeanStudentBodyUtilisationRatioTag,            getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio),
        (weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag,   getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest),
        (weightOfMinimiseDispersionOfStudentFreePeriodsPerDayTag,               getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay),
        (weightOfMinimiseDispersionOfTeacherFreePeriodsPerDayTag,               getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay),
        (weightOfMinimiseDispersionOfTeacherWorkloadTag,                        getWeightOfMinimiseDispersionOfTeacherWorkload),
        (weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag,                getWeightOfMinimiseMeanInterCampusMigrationsOfStudents),
        (weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag,                getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers),
        (weightOfMinimiseMeanLocationChangesOfTeachersTag,                      getWeightOfMinimiseMeanLocationChangesOfTeachers),
        (weightOfMinimiseMeanLocusOperandiOfTeachersTag,                        getWeightOfMinimiseMeanLocusOperandiOfTeachers),
        (weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledgeTag,         getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge),
        (weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag,     getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge),
        (weightOfMinimiseMeanStudentBodyCombinationsPerLessonTag,               getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson),
        (weightOfMinimiseRatioOfConsecutiveEqualLessonsTag,                     getWeightOfMinimiseRatioOfConsecutiveEqualLessons),
        (weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDayTag,           getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay)
 ]

-- | The weight of criteria used to select a /timetable/ from alternatives.
data TimetableCriteriaWeights w = MkTimetableCriteriaWeights {
        getWeightOfMaximiseComplianceWithFreePeriodPreferences                  :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when it complies with any /free time-slot preference/, of /student-bodies/ & /teacher/s.
        getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity        :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the mean over all /booking/s, of the ratio of the size of the /student-class/ to the /location/'s capacity, is greatest.
        getWeightOfMaximiseMeanStudentClassSize                                 :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the mean over the size of /student-class/es, relative to the total number of /student/s, is greatest.
        getWeightOfMaximiseSynchronisationOfSynchronisedCourses                 :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the /lesson/s of /synchronised course/s are consistently synchronised.
        getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio              :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the mean over all /student-bodies/, of their utilisation-ratio, is maximised. It doesn't distinguish between /core/ & /optional/ subjects like /minimiseMeanRatioOfIncompletelyBookedCoreKnowledge/ & /minimiseMeanRatioOfIncompletelyBookedOptionalKnowledge/, but it does value partially booked /course/s.
        getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest     :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when /lesson/s are booked close to any /ideal timeslot-request/ for their /course/. CAVEAT: excessive weight makes it preferable to take the penalty from 'getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge' or 'getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge', by not scheduling a /lesson/ if the available /time-slot/s are far from any specified ideal.
        getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay                 :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when /student/s' free /period/s are spread evenly across their working-week.
        getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay                 :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when /teacher/s' free /period/s are spread evenly across their working-week.
        getWeightOfMinimiseDispersionOfTeacherWorkload                          :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the relative workload between all /teacher/s, is similar.
        getWeightOfMinimiseMeanInterCampusMigrationsOfStudents                  :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average over all /student/s, of the number of inter-/campus/ migrations in a week, is minimised.
        getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers                  :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average over all /teacher/s, of the number of inter-/campus/ migrations in a week, is minimised.
        getWeightOfMinimiseMeanLocationChangesOfTeachers                        :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average over all /teacher/s, of the number of changes in their /location/ in a week, is minimised; cf. /minimiseMeanLocusOperandiOfTeachers/, which merely counts the number of distinct /location/s used.
        getWeightOfMinimiseMeanLocusOperandiOfTeachers                          :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average over all /teacher/s, of the size of their locus-operandi, is minimised; based merely on the number of /location/s, rather than the distance or the number of changes, between them.
        getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge           :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average over all /student-bodies/, of the ratio of incompletely booked /core/ /knowledge-requirements/, to all requirements, is minimised. All incomplete /course/s are valued equally, regardless of the extent.
        getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge       :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average over all /student-bodies/, of the ratio of incompletely booked /optional/ /knowledge-requirements/, to all requirements, is minimised. All incomplete /course/s are valued equally, regardless of the extent; cf. /maximiseWeightedMeanStudentBodyUtilisationRatio/.
        getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson                 :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when the average number of combinations of /student-class/ per /lesson/-definition, is minimised; ideally a /student-body/ always studies a /subject/, in a class composed from the same other /student-bodies/.
        getWeightOfMinimiseRatioOfConsecutiveEqualLessons                       :: ExecutionConfiguration.CriterionWeight.CriterionWeight w,    -- ^ A /timetable/ is preferred, when blocks of consecutive identical /lesson/s are booked, whose length most closely corresponds, to that requested for the /course/ to which they belong.
        getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay             :: ExecutionConfiguration.CriterionWeight.CriterionWeight w     -- ^ A /timetable/ is preferred, when the /lesson/s of any /course/, are on any /day/, booked in one contiguous block.
} deriving (Eq, Show)

instance (Eq w, Num w) => ToolShed.SelfValidate.SelfValidator (TimetableCriteriaWeights w) where
        getErrors timetableCriteriaWeights      = ToolShed.SelfValidate.extractErrors [
                (
                        ExecutionConfiguration.CriterionWeight.areAllZero timetableCriteriaWeights,
                        "The weight of @ least one timetable-criterion must be non-zero"
                ) -- Pair.
         ]


instance Num w => Data.Default.Default (TimetableCriteriaWeights w) where
        def = MkTimetableCriteriaWeights {
                getWeightOfMaximiseComplianceWithFreePeriodPreferences                  = Data.Default.def,
                getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity        = Data.Default.def,
                getWeightOfMaximiseMeanStudentClassSize                                 = Data.Default.def,
                getWeightOfMaximiseSynchronisationOfSynchronisedCourses                 = Data.Default.def,
                getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio              = Data.Default.def,
                getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest     = Data.Default.def,
                getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay                 = Data.Default.def,
                getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay                 = Data.Default.def,
                getWeightOfMinimiseDispersionOfTeacherWorkload                          = Data.Default.def,
                getWeightOfMinimiseMeanInterCampusMigrationsOfStudents                  = Data.Default.def,
                getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers                  = Data.Default.def,
                getWeightOfMinimiseMeanLocationChangesOfTeachers                        = Data.Default.def,
                getWeightOfMinimiseMeanLocusOperandiOfTeachers                          = Data.Default.def,
                getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge           = Data.Default.def,
                getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge       = Data.Default.def,
                getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson                 = Data.Default.def,
                getWeightOfMinimiseRatioOfConsecutiveEqualLessons                       = Data.Default.def,
                getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay             = Data.Default.def
        }

instance (HXT.XmlPickler w, Ord w, Real w) => HXT.XmlPickler (TimetableCriteriaWeights w) where
        xpickle = HXT.xpDefault Data.Default.def . HXT.xpElem tag $ HXT.xpWrap (
                \(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) -> MkTimetableCriteriaWeights a b c d e f g h i j k l m n o p q r,      -- Construct from a tuple.
                \MkTimetableCriteriaWeights {
                        getWeightOfMaximiseComplianceWithFreePeriodPreferences                  = weightOfMaximiseComplianceWithFreePeriodPreferences,
                        getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity        = weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity,
                        getWeightOfMaximiseMeanStudentClassSize                                 = weightOfMaximiseMeanStudentClassSize,
                        getWeightOfMaximiseSynchronisationOfSynchronisedCourses                 = weightOfMaximiseSynchronisationOfSynchronisedCourses,
                        getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio              = weightOfMaximiseWeightedMeanStudentBodyUtilisationRatio,
                        getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest     = weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest,
                        getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay                 = weightOfMinimiseDispersionOfStudentFreePeriodsPerDay,
                        getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay                 = weightOfMinimiseDispersionOfTeacherFreePeriodsPerDay,
                        getWeightOfMinimiseDispersionOfTeacherWorkload                          = weightOfMinimiseDispersionOfTeacherWorkload,
                        getWeightOfMinimiseMeanInterCampusMigrationsOfStudents                  = weightOfMinimiseMeanInterCampusMigrationsOfStudents,
                        getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers                  = weightOfMinimiseMeanInterCampusMigrationsOfTeachers,
                        getWeightOfMinimiseMeanLocationChangesOfTeachers                        = weightOfMinimiseMeanLocationChangesOfTeachers,
                        getWeightOfMinimiseMeanLocusOperandiOfTeachers                          = weightOfMinimiseMeanLocusOperandiOfTeachers,
                        getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge           = weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge,
                        getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge       = weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge,
                        getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson                 = weightOfMinimiseMeanStudentBodyCombinationsPerLesson,
                        getWeightOfMinimiseRatioOfConsecutiveEqualLessons                       = weightOfMinimiseRatioOfConsecutiveEqualLessons,
                        getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay             = weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay
                } -> (
                        weightOfMaximiseComplianceWithFreePeriodPreferences,
                        weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity,
                        weightOfMaximiseMeanStudentClassSize,
                        weightOfMaximiseSynchronisationOfSynchronisedCourses,
                        weightOfMaximiseWeightedMeanStudentBodyUtilisationRatio,
                        weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest,
                        weightOfMinimiseDispersionOfStudentFreePeriodsPerDay,
                        weightOfMinimiseDispersionOfTeacherFreePeriodsPerDay,
                        weightOfMinimiseDispersionOfTeacherWorkload,
                        weightOfMinimiseMeanInterCampusMigrationsOfStudents,
                        weightOfMinimiseMeanInterCampusMigrationsOfTeachers,
                        weightOfMinimiseMeanLocationChangesOfTeachers,
                        weightOfMinimiseMeanLocusOperandiOfTeachers,
                        weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge,
                        weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge,
                        weightOfMinimiseMeanStudentBodyCombinationsPerLesson,
                        weightOfMinimiseRatioOfConsecutiveEqualLessons,
                        weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay
                ) -- Deconstruct into a tuple.
         ) $ HXT.xp18Tuple (
                xpickle' weightOfMaximiseComplianceWithFreePeriodPreferencesTag
         ) (
                xpickle' weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacityTag
         ) (
                xpickle' weightOfMaximiseMeanStudentClassSizeTag
         ) (
                xpickle' weightOfMaximiseSynchronisationOfSynchronisedCoursesTag
         ) (
                xpickle' weightOfMaximiseWeightedMeanStudentBodyUtilisationRatioTag
         ) (
                xpickle' weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag
         ) (
                xpickle' weightOfMinimiseDispersionOfStudentFreePeriodsPerDayTag
         ) (
                xpickle' weightOfMinimiseDispersionOfTeacherFreePeriodsPerDayTag
         ) (
                xpickle' weightOfMinimiseDispersionOfTeacherWorkloadTag
         ) (
                xpickle' weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag
         ) (
                xpickle' weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag
         ) (
                xpickle' weightOfMinimiseMeanLocationChangesOfTeachersTag
         ) (
                xpickle' weightOfMinimiseMeanLocusOperandiOfTeachersTag
         ) (
                xpickle' weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledgeTag
         ) (
                xpickle' weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag
         ) (
                xpickle' weightOfMinimiseMeanStudentBodyCombinationsPerLessonTag
         ) (
                xpickle' weightOfMinimiseRatioOfConsecutiveEqualLessonsTag
         ) (
                xpickle' weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDayTag
         ) where
                xpickle'        = HXT.xpDefault Data.Default.def . (`HXT.xpAttr` HXT.xpickle)

instance (Eq criterionWeight, Num criterionWeight) => ExecutionConfiguration.CriterionWeight.CriterionWeights (TimetableCriteriaWeights criterionWeight) where
        areAllZero timetableCriteriaWeights     = ExecutionConfiguration.CriterionWeight.areAllZero $ map (($ timetableCriteriaWeights) . snd) associationList

instance Control.DeepSeq.NFData criterionWeight => Control.DeepSeq.NFData (TimetableCriteriaWeights criterionWeight) where
        rnf (MkTimetableCriteriaWeights x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17)  = Control.DeepSeq.rnf [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17]

{- |
	* Returns the weighted sum of the specified criteria, divided by the sum of the weights.

	* The magnitude of each /criterion/ should be in the same range, so that none dominates the total, thus making the total a clear measure of the value attributed to each.

	* The magnitude of the value of each /criterion/ should be independent of the dimensions of the /timetable/ (/observerId/s, /day/s per week, /time-slot/s per day),
	so that should any of these change, the balance between criteria doesn't shift; i.e. they should be dimensionless.
-}
calculateWeightedMean :: (
        Fractional      weightedMean,
        Real            criterionValue,
        Real            criterionWeight
 )
        => TimetableCriteriaWeights criterionWeight
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /complianceWithFreePeriodPreferences/: maximum when each unallocated /time-slot/ complies with the preferences of both /student-bodies/ & /teacher/s.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /classSizeOverLocationCapacity/: maximum when /location/ which has been booked, is completely filled by the /student-class/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ A /timetable/ is preferred, when the mean over the size of /student-class/es, relative to the total number of /student/s, is greatest.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /synchronisationOfSynchronisedCourses/: maximum if the /lesson/s for all /synchronised course/s, are synchronised.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /weightedMeanStudentBodyUtilisationRatio/: maximum if all /student-bodies/ are fully utilised.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /averageAbsoluteDeviationFromIdealTimeslotRequest/: maximum if all /lesson/s, whose /course/ specifies an ideal /time-slot/, have been booked exactly on it, falling towards minimum as the mean absolute deviation increases.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /dispersionOfStudentFreePeriodsPerDay/: maximum when the deviation in the number of free /student-period/s per day is zero, falling towards minimum when they all fall on one /day/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /dispersionOfTeacherFreePeriodsPerDay/: maximum when the deviation in the number of free /teacher-period/s per day is zero, falling towards minimum when they all fall on one /day/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /dispersionOfTeacherWorkload/: maximum when the deviation in the /workload/ between /teacher/s is zero, falling towards minimum as their workloads move away from the mean.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanInterCampusMigrationsOfStudents/: maximum when each /student/ migrates to a different /campus/ between each /timeslot/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanInterCampusMigrationsOfTeachers/: maximum when each /teacher/ migrates to a different /campus/ between each /timeslot/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanLocationChangesOfTeachers/: maximum when the location of each /teacher/ changes at every /time-slot/, falling towards minimum as each /lesson/ is taught at the same /location/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanLocusOperandiOfTeachers/: maximum when the locus-operandi of each /teacher/ is just one location, falling towards minimum as each /lesson/ taught is in a different /location/.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanRatioOfIncompletelyBookedCoreKnowledge/: maximum when all of a /student-body/'s required core /subject/s are completely booked, falling towards minimum when none are met.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanRatioOfIncompletelyBookedOptionalKnowledge/: maximum when all of a /student-body/'s required optional /subject/s are completely booked, falling towards minimum when none are met.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /meanStudentBodyCombinationsPerLesson/: maximum when each /student-body/ studies a /subject/, as a member of a constant /student-class/, falling towards minimum as the mean number of different /student-body/-combinations per /subject/ rises.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /ratioOfConsecutiveEqualLessons/: maximum when blocks of consecutive identical /lesson/s are booked, whose length equals that requested for the /course/ to which they belong.
        -> ExecutionConfiguration.Criterion.Criterion criterionValue            -- ^ /ratioOfSeparatedEqualLessonsWithinAnyDay/: maximum when only one contiguous block of /lesson/s in any /subject/, has been booked each /day/, falling towards minimum where separated blocks of equal /lesson/s occur within any one /day/.
        -> Control.Monad.Writer.Writer [Maybe criterionValue] weightedMean      -- ^ The individual /criteria/ values, & their /weighted mean/.
calculateWeightedMean MkTimetableCriteriaWeights {
        getWeightOfMaximiseComplianceWithFreePeriodPreferences                  = weightOfMaximiseComplianceWithFreePeriodPreferences,
        getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity        = weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity,
        getWeightOfMaximiseMeanStudentClassSize                                 = weightOfMaximiseMeanStudentClassSize,
        getWeightOfMaximiseSynchronisationOfSynchronisedCourses                 = weightOfMaximiseSynchronisationOfSynchronisedCourses,
        getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio              = weightOfMaximiseWeightedMeanStudentBodyUtilisationRatio,
        getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest     = weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest,
        getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay                 = weightOfMinimiseDispersionOfStudentFreePeriodsPerDay,
        getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay                 = weightOfMinimiseDispersionOfTeacherFreePeriodsPerDay,
        getWeightOfMinimiseDispersionOfTeacherWorkload                          = weightOfMinimiseDispersionOfTeacherWorkload,
        getWeightOfMinimiseMeanInterCampusMigrationsOfStudents                  = weightOfMinimiseMeanInterCampusMigrationsOfStudents,
        getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers                  = weightOfMinimiseMeanInterCampusMigrationsOfTeachers,
        getWeightOfMinimiseMeanLocationChangesOfTeachers                        = weightOfMinimiseMeanLocationChangesOfTeachers,
        getWeightOfMinimiseMeanLocusOperandiOfTeachers                          = weightOfMinimiseMeanLocusOperandiOfTeachers,
        getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge           = weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge,
        getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge       = weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge,
        getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson                 = weightOfMinimiseMeanStudentBodyCombinationsPerLesson,
        getWeightOfMinimiseRatioOfConsecutiveEqualLessons                       = weightOfMinimiseRatioOfConsecutiveEqualLessons,
        getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay             = weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay
} complianceWithFreePeriodPreferences meanRatioOfStudentClassSizeToLocationCapacity meanStudentClassSize synchronisationOfSynchronisedCourses weightedMeanStudentBodyUtilisationRatio averageAbsoluteDeviationFromIdealTimeslotRequest dispersionOfStudentFreePeriodsPerDay dispersionOfTeacherFreePeriodsPerDay dispersionOfTeacherWorkload meanInterCampusMigrationsOfStudents meanInterCampusMigrationsOfTeachers meanLocationChangesOfTeachers meanLocusOperandiOfTeachers meanRatioOfIncompletelyBookedCoreKnowledge meanRatioOfIncompletelyBookedOptionalKnowledge meanStudentBodyCombinationsPerLesson ratioOfConsecutiveEqualLessons ratioOfSeparatedEqualLessonsWithinAnyDay   = ExecutionConfiguration.Criterion.calculateWeightedMean [
        (complianceWithFreePeriodPreferences,                   weightOfMaximiseComplianceWithFreePeriodPreferences),
        (meanRatioOfStudentClassSizeToLocationCapacity,         weightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity),
        (meanStudentClassSize,                                  weightOfMaximiseMeanStudentClassSize),
        (synchronisationOfSynchronisedCourses,                  weightOfMaximiseSynchronisationOfSynchronisedCourses),
        (weightedMeanStudentBodyUtilisationRatio,               weightOfMaximiseWeightedMeanStudentBodyUtilisationRatio),
        (averageAbsoluteDeviationFromIdealTimeslotRequest,      weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest),
        (dispersionOfStudentFreePeriodsPerDay,                  weightOfMinimiseDispersionOfStudentFreePeriodsPerDay),
        (dispersionOfTeacherFreePeriodsPerDay,                  weightOfMinimiseDispersionOfTeacherFreePeriodsPerDay),
        (dispersionOfTeacherWorkload,                           weightOfMinimiseDispersionOfTeacherWorkload),
        (meanInterCampusMigrationsOfStudents,                   weightOfMinimiseMeanInterCampusMigrationsOfStudents),
        (meanInterCampusMigrationsOfTeachers,                   weightOfMinimiseMeanInterCampusMigrationsOfTeachers),
        (meanLocationChangesOfTeachers,                         weightOfMinimiseMeanLocationChangesOfTeachers),
        (meanLocusOperandiOfTeachers,                           weightOfMinimiseMeanLocusOperandiOfTeachers),
        (meanRatioOfIncompletelyBookedCoreKnowledge,            weightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge),
        (meanRatioOfIncompletelyBookedOptionalKnowledge,        weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge),
        (meanStudentBodyCombinationsPerLesson,                  weightOfMinimiseMeanStudentBodyCombinationsPerLesson),
        (ratioOfConsecutiveEqualLessons,                        weightOfMinimiseRatioOfConsecutiveEqualLessons),
        (ratioOfSeparatedEqualLessonsWithinAnyDay,              weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay)
 ]

-- | The type of a function which mutates timetable-criteria weights.
type Mutator w  = TimetableCriteriaWeights w -> TimetableCriteriaWeights w

-- Mutator.
zeroWeightOfMaximiseComplianceWithFreePeriodPreferences :: Num w => Mutator w
zeroWeightOfMaximiseComplianceWithFreePeriodPreferences lessonCriteriaWeights   = lessonCriteriaWeights {
        getWeightOfMaximiseComplianceWithFreePeriodPreferences  = minBound
}

-- Mutator.
zeroWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity :: Num w => Mutator w
zeroWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity lessonCriteriaWeights = lessonCriteriaWeights {
        getWeightOfMaximiseMeanRatioOfStudentClassSizeToLocationCapacity        = minBound
}

-- Mutator.
zeroWeightOfMaximiseMeanStudentClassSize :: Num w => Mutator w
zeroWeightOfMaximiseMeanStudentClassSize lessonCriteriaWeights  = lessonCriteriaWeights {
        getWeightOfMaximiseMeanStudentClassSize = minBound
}

-- Mutator.
zeroWeightOfMaximiseSynchronisationOfSynchronisedCourses :: Num w => Mutator w
zeroWeightOfMaximiseSynchronisationOfSynchronisedCourses lessonCriteriaWeights  = lessonCriteriaWeights {
        getWeightOfMaximiseSynchronisationOfSynchronisedCourses = minBound
}

-- Mutator.
zeroWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio :: Num w => Mutator w
zeroWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio lessonCriteriaWeights       = lessonCriteriaWeights {
        getWeightOfMaximiseWeightedMeanStudentBodyUtilisationRatio      = minBound
}

-- Mutator.
zeroWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest :: Num w => Mutator w
zeroWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest lessonCriteriaWeights      = lessonCriteriaWeights {
        getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest     = minBound
}

-- Mutator.
zeroWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay :: Num w => Mutator w
zeroWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay lessonCriteriaWeights  = lessonCriteriaWeights {
        getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay = minBound
}

-- Mutator.
zeroWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay :: Num w => Mutator w
zeroWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay lessonCriteriaWeights  = lessonCriteriaWeights {
        getWeightOfMinimiseDispersionOfTeacherFreePeriodsPerDay = minBound
}

-- Mutator.
zeroWeightOfMinimiseDispersionOfTeacherWorkload :: Num w => Mutator w
zeroWeightOfMinimiseDispersionOfTeacherWorkload lessonCriteriaWeights   = lessonCriteriaWeights {
        getWeightOfMinimiseDispersionOfTeacherWorkload  = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanInterCampusMigrationsOfStudents :: Num w => Mutator w
zeroWeightOfMinimiseMeanInterCampusMigrationsOfStudents lessonCriteriaWeights   = lessonCriteriaWeights {
        getWeightOfMinimiseMeanInterCampusMigrationsOfStudents  = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanInterCampusMigrationsOfTeachers :: Num w => Mutator w
zeroWeightOfMinimiseMeanInterCampusMigrationsOfTeachers lessonCriteriaWeights   = lessonCriteriaWeights {
        getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers  = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanLocationChangesOfTeachers :: Num w => Mutator w
zeroWeightOfMinimiseMeanLocationChangesOfTeachers lessonCriteriaWeights = lessonCriteriaWeights {
        getWeightOfMinimiseMeanLocationChangesOfTeachers        = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanLocusOperandiOfTeachers :: Num w => Mutator w
zeroWeightOfMinimiseMeanLocusOperandiOfTeachers lessonCriteriaWeights   = lessonCriteriaWeights {
        getWeightOfMinimiseMeanLocusOperandiOfTeachers  = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge :: Num w => Mutator w
zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge lessonCriteriaWeights    = lessonCriteriaWeights {
        getWeightOfMinimiseMeanRatioOfIncompletelyBookedCoreKnowledge   = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge :: Num w => Mutator w
zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge lessonCriteriaWeights        = lessonCriteriaWeights {
        getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge       = minBound
}

-- Mutator.
zeroWeightOfMinimiseMeanStudentBodyCombinationsPerLesson :: Num w => Mutator w
zeroWeightOfMinimiseMeanStudentBodyCombinationsPerLesson lessonCriteriaWeights  = lessonCriteriaWeights {
        getWeightOfMinimiseMeanStudentBodyCombinationsPerLesson = minBound
}

-- Mutator.
zeroWeightOfMinimiseRatioOfConsecutiveEqualLessons :: Num w => Mutator w
zeroWeightOfMinimiseRatioOfConsecutiveEqualLessons lessonCriteriaWeights        = lessonCriteriaWeights {
        getWeightOfMinimiseRatioOfConsecutiveEqualLessons       = minBound
}

-- Mutator.
zeroWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay :: Num w => Mutator w
zeroWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay lessonCriteriaWeights      = lessonCriteriaWeights {
        getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay     = minBound
}