packages feed

ideas-0.5.8: src/Service/ExerciseList.hs

{-# LANGUAGE ExistentialQuantification, Rank2Types #-}
-----------------------------------------------------------------------------
-- Copyright 2009, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Service.ExerciseList 
   ( exerciseList, findExercises, getExercise
   , openMathExercises, findOpenMathExercises, getOpenMathExercise
   , OpenMathExercise(..)
   , resolveExerciseCode
   ) where

import Common.Utils (Some(..))
import Common.Exercise
import Data.Char
import Domain.Math.Expr
import qualified Domain.LinearAlgebra as LA
import qualified Domain.Logic as Logic
import qualified Domain.RelationAlgebra as RA
import qualified Domain.Math.DerivativeExercise as Math
import qualified Domain.Math.Numeric.Exercises as Math
import qualified Domain.Math.Equation.CoverUpExercise as Math
import qualified Domain.Math.Polynomial.Exercises as Math

-- List with all known exercises
exerciseList :: [Some Exercise]
exerciseList = 
   [ -- logic and relation-algebra
     Some Logic.dnfExercise
   , Some Logic.dnfUnicodeExercise
   , Some RA.cnfExercise
   ] ++
   [ Some e | OMEX e <- openMathExercises ]

-----------------------------------------------------------------------------
-- All mathematical exercises are supported by the OpenMath standard, and 
-- require an extra type constraint

data OpenMathExercise = forall a . IsExpr a => OMEX (Exercise a)
   
openMathExercises :: [OpenMathExercise]
openMathExercises = 
   [ -- basic math
     OMEX Math.naturalExercise, OMEX Math.integerExercise
   , OMEX Math.rationalExercise, OMEX Math.fractionExercise
   , OMEX Math.coverUpExercise
   , OMEX Math.linearExercise
   , OMEX Math.quadraticExercise
   , OMEX Math.higherDegreeExercise
   , OMEX Math.derivativeExercise
     -- linear algebra
   , OMEX LA.gramSchmidtExercise
   , OMEX LA.linearSystemExercise
   , OMEX LA.gaussianElimExercise
   , OMEX LA.systemWithMatrixExercise
   ]
   
-----------------------------------------------------------------------------
-- Utility functions for finding an exercise

resolveExerciseCode :: Monad m => String -> m ExerciseCode
resolveExerciseCode txt = 
   case findExercises (\ex -> show (exerciseCode ex) ~= txt) of
      [Some ex] -> return (exerciseCode ex)
      _         -> fail $ "Failed to resolve the exercise code " ++ show txt
 where
   s ~= t = f s == f t 
   f = map toLower . filter isAlphaNum

findExercises :: (forall a . Exercise a -> Bool) -> [Some Exercise]
findExercises p = [ Some e | Some e <- exerciseList, p e ]

getExercise :: Monad m => ExerciseCode -> m (Some Exercise)
getExercise code = 
   case findExercises ((==code) . exerciseCode) of
      [hd] -> return hd
      []   -> fail $ "No exercise with code "   ++ show code
      _    -> fail $ "Ambiguous exercise code " ++ show code
      
findOpenMathExercises :: (forall a . Exercise a -> Bool) -> [OpenMathExercise]
findOpenMathExercises p = [ OMEX e | OMEX e <- openMathExercises, p e ]

getOpenMathExercise :: Monad m => ExerciseCode -> m OpenMathExercise
getOpenMathExercise code = 
   case findOpenMathExercises ((==code) . exerciseCode) of
      [hd] -> return hd
      []   -> fail $ "No exercise with code "   ++ show code
      _    -> fail $ "Ambiguous exercise code " ++ show code