ideas 0.6 → 0.7
raw patch · 180 files changed
+11604/−7617 lines, 180 filesdep +uniplate
Dependencies added: uniplate
Files
- ideas.cabal +44/−24
- src/Common/Apply.hs +0/−57
- src/Common/Classes.hs +112/−0
- src/Common/Context.hs +54/−48
- src/Common/Derivation.hs +21/−5
- src/Common/Exercise.hs +147/−179
- src/Common/Id.hs +163/−0
- src/Common/Library.hs +54/−0
- src/Common/Navigator.hs +49/−33
- src/Common/Rewriting.hs +9/−8
- src/Common/Rewriting/AC.hs +40/−117
- src/Common/Rewriting/Axioms.hs +147/−0
- src/Common/Rewriting/Confluence.hs +163/−0
- src/Common/Rewriting/Difference.hs +51/−40
- src/Common/Rewriting/Group.hs +247/−0
- src/Common/Rewriting/MetaVar.hs +0/−76
- src/Common/Rewriting/Operator.hs +117/−0
- src/Common/Rewriting/RewriteRule.hs +111/−126
- src/Common/Rewriting/Substitution.hs +19/−28
- src/Common/Rewriting/Term.hs +153/−52
- src/Common/Rewriting/Unification.hs +59/−37
- src/Common/Strategy.hs +10/−9
- src/Common/Strategy/Abstract.hs +52/−56
- src/Common/Strategy/BiasedChoice.hs +0/−106
- src/Common/Strategy/Combinators.hs +51/−13
- src/Common/Strategy/Configuration.hs +28/−20
- src/Common/Strategy/Core.hs +67/−100
- src/Common/Strategy/Grammar.hs +0/−367
- src/Common/Strategy/Location.hs +46/−86
- src/Common/Strategy/Parsing.hs +191/−0
- src/Common/Strategy/Prefix.hs +31/−56
- src/Common/StringRef.hs +128/−0
- src/Common/TestSuite.hs +385/−0
- src/Common/Transformation.hs +92/−92
- src/Common/Traversable.hs +0/−135
- src/Common/Uniplate.hs +54/−52
- src/Common/Utils.hs +18/−44
- src/Common/View.hs +99/−73
- src/Documentation/DefaultPage.hs +31/−64
- src/Documentation/DerivationUnitTests.hs +39/−0
- src/Documentation/ExercisePage.hs +135/−107
- src/Documentation/LatexRules.hs +0/−140
- src/Documentation/Make.hs +39/−15
- src/Documentation/OverviewPages.hs +19/−18
- src/Documentation/RulePage.hs +119/−0
- src/Documentation/RulePresenter.hs +119/−0
- src/Documentation/SelfCheck.hs +65/−95
- src/Documentation/ServicePage.hs +59/−39
- src/Documentation/TestsPage.hs +52/−64
- src/Domain/LinearAlgebra/Checks.hs +9/−9
- src/Domain/LinearAlgebra/EquationsRules.hs +36/−25
- src/Domain/LinearAlgebra/Exercises.hs +11/−36
- src/Domain/LinearAlgebra/LinearSystem.hs +17/−14
- src/Domain/LinearAlgebra/LinearView.hs +20/−58
- src/Domain/LinearAlgebra/Matrix.hs +58/−27
- src/Domain/LinearAlgebra/MatrixRules.hs +7/−8
- src/Domain/LinearAlgebra/Strategies.hs +18/−27
- src/Domain/LinearAlgebra/Symbols.hs +0/−65
- src/Domain/LinearAlgebra/Vector.hs +45/−5
- src/Domain/Logic.hs +11/−12
- src/Domain/Logic/BuggyRules.hs +31/−23
- src/Domain/Logic/Examples.hs +49/−0
- src/Domain/Logic/Exercises.hs +6/−6
- src/Domain/Logic/FeedbackText.hs +15/−14
- src/Domain/Logic/Formula.hs +65/−59
- src/Domain/Logic/GeneralizedRules.hs +5/−1
- src/Domain/Logic/Generator.hs +29/−25
- src/Domain/Logic/Parser.hs +36/−14
- src/Domain/Logic/Proofs.hs +319/−0
- src/Domain/Logic/Rules.hs +25/−11
- src/Domain/Logic/Strategies.hs +102/−100
- src/Domain/Logic/Views.hs +96/−0
- src/Domain/Math/Approximation.hs +2/−2
- src/Domain/Math/Clipboard.hs +8/−3
- src/Domain/Math/Data/Interval.hs +43/−42
- src/Domain/Math/Data/OrList.hs +21/−21
- src/Domain/Math/Data/Polynomial.hs +18/−14
- src/Domain/Math/Data/PrimeFactors.hs +30/−5
- src/Domain/Math/Data/Relation.hs +48/−40
- src/Domain/Math/Data/SquareRoot.hs +8/−8
- src/Domain/Math/Derivative/Exercises.hs +219/−0
- src/Domain/Math/Derivative/Rules.hs +210/−0
- src/Domain/Math/Derivative/Strategies.hs +102/−0
- src/Domain/Math/DerivativeExercise.hs +0/−66
- src/Domain/Math/DerivativeRules.hs +0/−110
- src/Domain/Math/Equation/BalanceRules.hs +37/−0
- src/Domain/Math/Equation/CoverUpExercise.hs +12/−13
- src/Domain/Math/Equation/CoverUpRules.hs +68/−50
- src/Domain/Math/Equation/Views.hs +6/−6
- src/Domain/Math/Examples/DWO1.hs +1/−0
- src/Domain/Math/Examples/DWO3.hs +1/−0
- src/Domain/Math/Examples/DWO4.hs +502/−0
- src/Domain/Math/Examples/DWO5.hs +167/−0
- src/Domain/Math/Expr.hs +3/−3
- src/Domain/Math/Expr/Data.hs +63/−82
- src/Domain/Math/Expr/Parser.hs +10/−5
- src/Domain/Math/Expr/Symbolic.hs +0/−121
- src/Domain/Math/Expr/Symbols.hs +94/−66
- src/Domain/Math/Expr/Views.hs +32/−22
- src/Domain/Math/Numeric/Exercises.hs +11/−10
- src/Domain/Math/Numeric/Laws.hs +60/−63
- src/Domain/Math/Numeric/Rules.hs +21/−18
- src/Domain/Math/Numeric/Strategies.hs +46/−86
- src/Domain/Math/Numeric/Tests.hs +51/−46
- src/Domain/Math/Numeric/Views.hs +23/−25
- src/Domain/Math/Polynomial/BuggyRules.hs +401/−12
- src/Domain/Math/Polynomial/CleanUp.hs +104/−241
- src/Domain/Math/Polynomial/Equivalence.hs +43/−29
- src/Domain/Math/Polynomial/Exercises.hs +71/−61
- src/Domain/Math/Polynomial/IneqExercises.hs +99/−64
- src/Domain/Math/Polynomial/LeastCommonMultiple.hs +138/−0
- src/Domain/Math/Polynomial/RationalExercises.hs +313/−0
- src/Domain/Math/Polynomial/RationalRules.hs +186/−0
- src/Domain/Math/Polynomial/Rules.hs +340/−318
- src/Domain/Math/Polynomial/Strategies.hs +126/−124
- src/Domain/Math/Polynomial/Tests.hs +11/−23
- src/Domain/Math/Polynomial/Views.hs +97/−106
- src/Domain/Math/Power/Equation/Exercises.hs +101/−0
- src/Domain/Math/Power/Equation/NormViews.hs +215/−0
- src/Domain/Math/Power/Equation/Rules.hs +123/−0
- src/Domain/Math/Power/Equation/Strategies.hs +115/−0
- src/Domain/Math/Power/Exercises.hs +58/−51
- src/Domain/Math/Power/NormViews.hs +147/−0
- src/Domain/Math/Power/OldViews.hs +55/−0
- src/Domain/Math/Power/Rules.hs +230/−193
- src/Domain/Math/Power/Strategies.hs +48/−119
- src/Domain/Math/Power/Utils.hs +185/−0
- src/Domain/Math/Power/Views.hs +101/−295
- src/Domain/Math/Simplification.hs +50/−27
- src/Domain/Math/SquareRoot/Tests.hs +2/−2
- src/Domain/Math/SquareRoot/Views.hs +1/−1
- src/Domain/RegularExpr/Definitions.hs +0/−70
- src/Domain/RegularExpr/Exercises.hs +0/−75
- src/Domain/RegularExpr/Expr.hs +0/−175
- src/Domain/RegularExpr/Parser.hs +0/−39
- src/Domain/RegularExpr/Strategy.hs +0/−100
- src/Domain/RelationAlgebra.hs +1/−1
- src/Domain/RelationAlgebra/Equivalence.hs +0/−189
- src/Domain/RelationAlgebra/Exercises.hs +3/−3
- src/Domain/RelationAlgebra/Formula.hs +46/−43
- src/Domain/RelationAlgebra/Generator.hs +23/−14
- src/Domain/RelationAlgebra/Parser.hs +5/−1
- src/Domain/RelationAlgebra/Rules.hs +46/−20
- src/Main.hs +3/−2
- src/Main/ExerciseList.hs +0/−100
- src/Main/IDEAS.hs +130/−0
- src/Main/LoggingDatabase.hs +2/−2
- src/Main/Options.hs +24/−13
- src/Main/Revision.hs +6/−3
- src/Service/BasicServices.hs +153/−0
- src/Service/Diagnose.hs +67/−37
- src/Service/DomainReasoner.hs +72/−15
- src/Service/Evaluator.hs +100/−0
- src/Service/ExercisePackage.hs +42/−15
- src/Service/FeedbackText.hs +44/−54
- src/Service/ModeJSON.hs +68/−59
- src/Service/ModeXML.hs +137/−134
- src/Service/ProblemDecomposition.hs +85/−207
- src/Service/Request.hs +2/−2
- src/Service/RulesInfo.hs +21/−26
- src/Service/ServiceList.hs +59/−62
- src/Service/State.hs +72/−0
- src/Service/StrategyInfo.hs +26/−38
- src/Service/Submit.hs +34/−7
- src/Service/TypedAbstractService.hs +0/−152
- src/Service/TypedExample.hs +20/−30
- src/Service/Types.hs +114/−114
- src/Text/HTML.hs +56/−10
- src/Text/JSON.hs +13/−17
- src/Text/OpenMath/Object.hs +15/−12
- src/Text/OpenMath/Tests.hs +50/−0
- src/Text/Parsing.hs +27/−22
- src/Text/Scanning.hs +12/−9
- src/Text/UTF8.hs +3/−5
- src/Text/XML.hs +1/−1
- src/Text/XML/Interface.hs +14/−7
- src/Text/XML/ParseLib.hs +4/−4
- src/Text/XML/Parser.hs +18/−13
- src/Text/XML/TestSuite.hs +0/−1
- src/Text/XML/Unicode.hs +10/−1
ideas.cabal view
@@ -1,7 +1,7 @@ name: ideas-version: 0.6+version: 0.7 synopsis: Feedback services for intelligent tutoring systems-homepage: http://ideas.cs.uu.nl/+homepage: http://ideas.cs.uu.nl/www/ description: ideas provides feedback services to intelligent tutoring systems such as @@ -18,47 +18,54 @@ extra-source-files: CREDITS.txt build-type: Simple cabal-version: >= 1.8.0.2-tested-with: GHC == 6.12.1+tested-with: GHC == 6.10.1, GHC == 6.12.1 -------------------------------------------------------------------------------- Executable ideas Main-is: Main.hs- ghc-options: -W -fwarn-tabs -fwarn-duplicate-exports+ ghc-options: -Wall hs-source-dirs: src- other-modules: - Common.Apply+ other-modules:+ Common.Classes Common.Context Common.Derivation Common.Exercise+ Common.Id+ Common.Library Common.Navigator Common.Rewriting.AC+ Common.Rewriting.Axioms+ Common.Rewriting.Confluence Common.Rewriting.Difference- Common.Rewriting.MetaVar+ Common.Rewriting.Group+ Common.Rewriting.Operator Common.Rewriting.RewriteRule Common.Rewriting.Substitution Common.Rewriting.Term Common.Rewriting.Unification Common.Rewriting Common.Strategy.Abstract- Common.Strategy.BiasedChoice Common.Strategy.Combinators Common.Strategy.Configuration Common.Strategy.Core- Common.Strategy.Grammar Common.Strategy.Location+ Common.Strategy.Parsing Common.Strategy.Prefix Common.Strategy+ Common.StringRef+ Common.TestSuite Common.Transformation- Common.Traversable Common.Uniplate Common.Utils Common.View Documentation.DefaultPage+ Documentation.DerivationUnitTests Documentation.ExercisePage- Documentation.LatexRules Documentation.Make Documentation.OverviewPages+ Documentation.RulePage+ Documentation.RulePresenter Documentation.SelfCheck Documentation.ServicePage Documentation.TestsPage@@ -72,18 +79,20 @@ Domain.LinearAlgebra.MatrixRules Domain.LinearAlgebra.Parser Domain.LinearAlgebra.Strategies- Domain.LinearAlgebra.Symbols Domain.LinearAlgebra.Vector Domain.LinearAlgebra Domain.Logic.BuggyRules+ Domain.Logic.Examples Domain.Logic.Exercises Domain.Logic.FeedbackText Domain.Logic.Formula Domain.Logic.GeneralizedRules Domain.Logic.Generator Domain.Logic.Parser+ Domain.Logic.Proofs Domain.Logic.Rules Domain.Logic.Strategies+ Domain.Logic.Views Domain.Logic Domain.Math.Approximation Domain.Math.Clipboard@@ -93,17 +102,20 @@ Domain.Math.Data.PrimeFactors Domain.Math.Data.Relation Domain.Math.Data.SquareRoot- Domain.Math.DerivativeExercise- Domain.Math.DerivativeRules+ Domain.Math.Derivative.Exercises+ Domain.Math.Derivative.Rules+ Domain.Math.Derivative.Strategies+ Domain.Math.Equation.BalanceRules Domain.Math.Equation.CoverUpExercise Domain.Math.Equation.CoverUpRules Domain.Math.Equation.Views Domain.Math.Examples.DWO1 Domain.Math.Examples.DWO2 Domain.Math.Examples.DWO3+ Domain.Math.Examples.DWO4+ Domain.Math.Examples.DWO5 Domain.Math.Expr.Data Domain.Math.Expr.Parser- Domain.Math.Expr.Symbolic Domain.Math.Expr.Symbols Domain.Math.Expr.Views Domain.Math.Expr@@ -120,23 +132,27 @@ Domain.Math.Polynomial.Exercises Domain.Math.Polynomial.Generators Domain.Math.Polynomial.IneqExercises+ Domain.Math.Polynomial.LeastCommonMultiple+ Domain.Math.Polynomial.RationalExercises+ Domain.Math.Polynomial.RationalRules Domain.Math.Polynomial.Rules Domain.Math.Polynomial.Strategies Domain.Math.Polynomial.Tests Domain.Math.Polynomial.Views+ Domain.Math.Power.Equation.Exercises+ Domain.Math.Power.Equation.NormViews+ Domain.Math.Power.Equation.Rules+ Domain.Math.Power.Equation.Strategies Domain.Math.Power.Exercises+ Domain.Math.Power.NormViews+ Domain.Math.Power.OldViews Domain.Math.Power.Rules Domain.Math.Power.Strategies+ Domain.Math.Power.Utils Domain.Math.Power.Views Domain.Math.Simplification Domain.Math.SquareRoot.Tests Domain.Math.SquareRoot.Views- Domain.RegularExpr.Definitions- Domain.RegularExpr.Exercises- Domain.RegularExpr.Expr- Domain.RegularExpr.Parser- Domain.RegularExpr.Strategy- Domain.RelationAlgebra.Equivalence Domain.RelationAlgebra.Exercises Domain.RelationAlgebra.Formula Domain.RelationAlgebra.Generator@@ -144,13 +160,15 @@ Domain.RelationAlgebra.Rules Domain.RelationAlgebra.Strategies Domain.RelationAlgebra- Main.ExerciseList+ Main.IDEAS Main.LoggingDatabase Main.Options Main.Revision Main+ Service.BasicServices Service.Diagnose Service.DomainReasoner+ Service.Evaluator Service.ExercisePackage Service.FeedbackText Service.ModeJSON@@ -159,9 +177,9 @@ Service.Request Service.RulesInfo Service.ServiceList+ Service.State Service.StrategyInfo Service.Submit- Service.TypedAbstractService Service.TypedExample Service.Types Text.HTML@@ -181,6 +199,7 @@ Text.OpenMath.MakeSymbols Text.OpenMath.Object Text.OpenMath.Symbol+ Text.OpenMath.Tests Text.Parsing Text.Scanning Text.UTF8@@ -202,7 +221,8 @@ uulib, filepath, parsec,- old-time+ old-time,+ uniplate --------------------------------------------------------------------------------
− src/Common/Apply.hs
@@ -1,57 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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)------ This module defines the type class Apply and some related utility functions.----------------------------------------------------------------------------------module Common.Apply where--import Common.Utils (safeHead)-import Control.Monad (join)-import Data.Maybe (isJust, fromMaybe)----- | A type class for functors that can be applied to a value. Transformation, Rule, and--- Strategy are all instances of this type class. Minimal complete definition: only one of--- the two member functions should be defined.-class Apply t where- apply :: t a -> a -> Maybe a -- ^ Returns zero or one results- applyAll :: t a -> a -> [a] -- ^ Returns zero or more results- -- default definitions- apply ta = safeHead . applyAll ta- applyAll ta = maybe [] return . apply ta---- | Checks whether the functor is applicable (at least one result)-applicable :: Apply t => t a -> a -> Bool-applicable ta = isJust . apply ta---- | If not applicable, return the current value (as default)-applyD :: Apply t => t a -> a -> a-applyD ta a = fromMaybe a (apply ta a)---- | Same as apply, except that the result (at most one) is returned in some monad-applyM :: (Apply t, Monad m) => t a -> a -> m a-applyM ta = maybe (fail "applyM") return . apply ta- --- | Apply a list of steps, and return at most one result-applyList :: Apply t => [t a] -> a -> Maybe a-applyList xs a = foldl (\ma t -> join $ fmap (apply t) ma) (Just a) xs---- | Apply a list of steps, and return all results-applyListAll :: Apply t => [t a] -> a -> [a]-applyListAll xs a = foldl (\ma t -> concatMap (applyAll t) ma) [a] xs---- | Apply a list of steps, and if there is no result, return the current value (as default)-applyListD :: Apply t => [t a] -> a -> a-applyListD xs a = foldl (\a t -> applyD t a) a xs---- Apply a list of steps, and return the result (at most one) in some monad-applyListM :: (Apply t, Monad m) => [t a] -> a -> m a-applyListM xs a = foldl (\ma t -> ma >>= applyM t) (return a) xs
+ src/Common/Classes.hs view
@@ -0,0 +1,112 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- Type classes and instances.+--+-----------------------------------------------------------------------------+module Common.Classes + ( Apply(..), applicable, applyD, applyM+ , Switch(..), Crush(..), Zip(..)+ ) where++import Common.Utils (safeHead)+import Data.Maybe+import Control.Monad.Identity+import qualified Data.IntMap as IM +import qualified Data.Map as M++-----------------------------------------------------------+-- * Type class |Apply|++-- | A type class for functors that can be applied to a value. Transformation, Rule, and+-- Strategy are all instances of this type class. Minimal complete definition: only one of+-- the two member functions should be defined.+class Apply t where+ apply :: t a -> a -> Maybe a -- ^ Returns zero or one results+ applyAll :: t a -> a -> [a] -- ^ Returns zero or more results+ -- default definitions+ apply ta = safeHead . applyAll ta+ applyAll ta = maybe [] return . apply ta++-- | Checks whether the functor is applicable (at least one result)+applicable :: Apply t => t a -> a -> Bool+applicable ta = isJust . apply ta++-- | If not applicable, return the current value (as default)+applyD :: Apply t => t a -> a -> a+applyD ta a = fromMaybe a (apply ta a)++-- | Same as apply, except that the result (at most one) is returned in some monad+applyM :: (Apply t, Monad m) => t a -> a -> m a+applyM ta = maybe (fail "applyM") return . apply ta++-----------------------------------------------------------+-- * Type class |Switch|++class Functor f => Switch f where+ switch :: Monad m => f (m a) -> m (f a)+ +instance Switch [] where+ switch = sequence++instance Switch Maybe where+ switch = maybe (return Nothing) (liftM Just)++instance Switch Identity where+ switch (Identity m) = liftM Identity m++instance Eq a => Switch (M.Map a) where+ switch m = do+ let (ns, ms) = unzip (M.toList m)+ as <- sequence ms + return $ M.fromAscList $ zip ns as++instance Switch IM.IntMap where+ switch m = do+ let (ns, ms) = unzip (IM.toList m)+ as <- sequence ms + return $ IM.fromAscList $ zip ns as++-----------------------------------------------------------+-- * Type class |Crush|++class Functor f => Crush f where+ crush :: f a -> [a]++instance Crush [] where+ crush = id++instance Crush Maybe where+ crush = maybe [] return++instance Crush Identity where+ crush = return . runIdentity++instance Crush (M.Map a) where+ crush = M.elems++instance Crush IM.IntMap where+ crush = IM.elems++-----------------------------------------------------------+-- * Type class |Zip|+ +class Functor f => Zip f where+ fzip :: f a -> f b -> f (a, b)+ fzipWith :: (a -> b -> c) -> f a -> f b -> f c+ -- default implementation+ fzip = fzipWith (,)+ fzipWith f a b = fmap (uncurry f) (fzip a b)++instance Zip [] where+ fzipWith = zipWith++instance Zip Maybe where+ fzipWith = liftM2
src/Common/Context.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XDeriveDataTypeable #-}+{-# LANGUAGE DeriveDataTypeable #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -22,17 +22,17 @@ -- * Variables , Var, newVar, makeVar -- * Lifting- , liftToContext, liftTransContext, contextView+ , liftToContext, liftTransContext+ , use, useC, termNavigator, applyTop -- * Context Monad- , ContextMonad, runCM, readVar, writeVar, modifyVar- , maybeCM, withCM, evalCM -- , listCM, runListCM, withListCM+ , ContextMonad, readVar, writeVar, modifyVar+ , maybeCM, withCM, evalCM ) where import Common.Navigator--import qualified Common.Navigator as Navigator+import Common.Rewriting import Common.Transformation-import Common.Utils (safeHead, commaList, readM)+import Common.Utils (commaList, readM) import Common.View import Control.Monad import Data.Maybe@@ -65,7 +65,7 @@ up (C env a) = liftM (C env) (up a) allDowns (C env a) = map (C env) (allDowns a) current (C _ a) = current a- location (C _ a) = Navigator.location a+ location (C _ a) = location a changeM f (C env a) = liftM (C env) (changeM f a) instance TypedNavigator Context where@@ -155,46 +155,65 @@ -- | Lift a rule to operate on a term in a context liftToContext :: Rule a -> Rule (Context a)-liftToContext = liftRuleIn thisView+liftToContext = liftRuleIn contextView liftTransContext :: Transformation a -> Transformation (Context a)-liftTransContext = liftTransIn thisView+liftTransContext = liftTransIn contextView -thisView :: View (Context a) (a, Context a)-thisView = makeView f g+-- | Apply a function at top-level. Afterwards, try to return the focus +-- to the old position+applyTop :: (a -> a) -> Context a -> Context a+applyTop f c = + case top c of + Just ok -> navigateTowards (location c) (change f ok)+ Nothing -> c++termNavigator :: IsTerm a => a -> Navigator a+termNavigator a = fromMaybe (noNavigator a) (make a) where- f ctx = current ctx >>= \a -> Just (a, ctx)- g = uncurry replace+ make = castT termView . viewNavigatorWith spineHoles . toTerm -contextView :: MonadPlus m => ViewM m a b -> ViewM m (Context a) (Context b)-contextView v = makeView f g+ spineHoles :: Term -> [(Term, Term -> Term)]+ spineHoles term+ | null xs = []+ | otherwise = (x, flip makeTerm xs) : zipWith f [0..] xs+ where+ (x, xs) = getSpine term+ f i y = (y, makeTerm x . changeAt i)+ changeAt i b = + case splitAt i xs of+ (ys, _:zs) -> ys ++ b:zs+ _ -> xs++use :: (IsTerm a, IsTerm b) => Rule a -> Rule (Context b)+use = useC . liftToContext++useC :: (IsTerm a, IsTerm b) => Rule (Context a) -> Rule (Context b)+useC = liftRule (makeView (castT termView) (fromJust . castT termView))++contextView :: View (Context a) (a, Context a)+contextView = newView "views.contextView" f g where- f ca = do- guard (isTop ca)- a <- leave ca- b <- match v a- return (newContext (getEnvironment ca) (noNavigator b))- g cb = fromJust $ do- guard (isTop cb)- b <- leave cb- let a = build v b- return (newContext (getEnvironment cb) (noNavigator a))+ f ctx = current ctx >>= \a -> Just (a, ctx)+ g = uncurry replace ---------------------------------------------------------- -- Context monad -newtype ContextMonad a = CM (Environment -> [(a, Environment)])+newtype ContextMonad a = CM { unCM :: Environment -> Maybe (a, Environment) } -withCM :: (a -> ContextMonad b) -> Context a -> Maybe (Context b)-withCM f c = fromContext c >>= \a -> runCM (f a) (getEnvironment c)+withCM :: (a -> ContextMonad a) -> Context a -> Maybe (Context a)+withCM f c = do + a0 <- current c+ (a, env) <- unCM (f a0) (getEnvironment c)+ let nav = replace a (getNavigator c)+ return (newContext env nav) evalCM :: (a -> ContextMonad b) -> Context a -> Maybe b-evalCM f c = withCM f c >>= fromContext--runCM :: ContextMonad a -> Environment -> Maybe (Context a)-runCM (CM f) env = do- (a, e) <- safeHead (f env)- return (newContext e (noNavigator a))+evalCM f c = do+ a0 <- current c+ (b, _) <- unCM (f a0) (getEnvironment c)+ return b instance Functor ContextMonad where fmap = liftM@@ -229,16 +248,3 @@ maybeCM :: Maybe a -> ContextMonad a maybeCM = maybe mzero return---{--listCM :: [a] -> ContextMonad a-listCM = foldr (mplus . return) mzero--withListCM :: (a -> ContextMonad b) -> Context a -> [Context b]-withListCM f c = runListCM (f (fromContext c)) (getEnvironment c)--runListCM :: ContextMonad a -> Environment -> [Context a]-runListCM (CM f) env = do- (a, e) <- f env- return (C e a) -}
src/Common/Derivation.hs view
@@ -22,9 +22,11 @@ , results, lengthMax -- * Adapters , restrictHeight, restrictWidth, commit- , mergeSteps, cutOnStep, mapSteps, mergeMaybeSteps, changeLabel+ , mergeSteps, cutOnStep, mapSteps, mergeMaybeSteps+ , changeLabel, sortTree -- * Query a derivation , isEmpty, derivationLength, terms, steps, triples, filterDerivation+ , mapStepsDerivation, derivationM -- * Conversions , derivation, randomDerivation, derivations ) where@@ -32,6 +34,7 @@ import Common.Utils (safeHead) import Control.Arrow import Control.Monad+import Data.List import Data.Maybe import System.Random @@ -143,8 +146,14 @@ changeLabel :: (l -> m) -> DerivationTree l a -> DerivationTree m a changeLabel f = rec where- rec t = t {branches = map (\(l, st) -> (f l, rec st)) (branches t)}+ rec t = t {branches = map (f *** rec) (branches t)} +sortTree :: (l -> l -> Ordering) -> DerivationTree l a -> DerivationTree l a+sortTree f t = t {branches = change (branches t) }+ where+ change = map (second (sortTree f)) . sortBy cmp+ cmp (l1, _) (l2, _) = f l1 l2+ mergeMaybeSteps :: DerivationTree (Maybe s) a -> DerivationTree s a mergeMaybeSteps = mapSteps fromJust . mergeSteps isJust @@ -166,6 +175,9 @@ isEmpty :: Derivation s a -> Bool isEmpty (D _ xs) = null xs +mapStepsDerivation :: (s -> t) -> Derivation s a -> Derivation t a+mapStepsDerivation f (D a xs) = D a (map (first f) xs)+ -- | Returns the number of steps in a derivation derivationLength :: Derivation s a -> Int derivationLength (D _ xs) = length xs@@ -187,6 +199,10 @@ filterDerivation :: (s -> a -> Bool) -> Derivation s a -> Derivation s a filterDerivation p (D a xs) = D a (filter (uncurry p) xs) +-- | Apply a monadic function to each term, and to each step+derivationM :: Monad m => (s -> m ()) -> (a -> m ()) -> Derivation s a -> m ()+derivationM f g (D a xs) = g a >> mapM_ (\(s, b) -> f s >> g b) xs+ ----------------------------------------------------------------------------- -- Conversions from a derivation tree @@ -194,7 +210,7 @@ derivations :: DerivationTree s a -> Derivations s a derivations t = map (D (root t)) $ [ [] | endpoint t ] ++- [ ((r,a2):ys) | (r, st) <- branches t, D a2 ys <- derivations st ]+ [ (r,a2):ys | (r, st) <- branches t, D a2 ys <- derivations st ] -- | The first derivation (if any) derivation :: DerivationTree s a -> Maybe (Derivation s a)@@ -214,8 +230,8 @@ shuffle :: RandomGen g => g -> [a] -> ([a], g) shuffle g0 xs = rec g0 [] (length xs) xs where- rec g acc n xs = - case splitAt i xs of+ rec g acc n ys = + case splitAt i ys of (as, b:bs) -> rec g1 (b:acc) (n-1) (as++bs) _ -> (acc, g) where
src/Common/Exercise.hs view
@@ -14,45 +14,48 @@ module Common.Exercise ( -- * Exercises Exercise, testableExercise, makeExercise, emptyExercise- , description, exerciseCode, status, parser, prettyPrinter+ , exerciseId, status, parser, prettyPrinter , equivalence, similarity, isReady, isSuitable, eqWithContext- , strategy, navigation, canBeRestarted, extraRules+ , strategy, navigation, canBeRestarted, extraRules, ruleOrdering , difference, ordering, testGenerator, randomExercise, examples, getRule , simpleGenerator, useGenerator , randomTerm, randomTermWith, ruleset- , makeContext, inContext+ , makeContext, inContext, recognizeRule, ruleIsRecognized+ , ruleOrderingWith, ruleOrderingWithId -- * Exercise status , Status(..), isPublic, isPrivate- -- * Exercise codes- , ExerciseCode, noCode, makeCode, readCode, domain, identifier -- * Miscellaneous+ , prettyPrinterContext , equivalenceContext, restrictGenerator , showDerivation, printDerivation+ , ExerciseDerivation, defaultDerivation, derivationDiffEnv , checkExercise, checkParserPretty- , checkExamples, generate+ , checkExamples, exerciseTestSuite+ , module Common.Id -- for backwards compatibility ) where -import Common.Apply+import Common.Classes import Common.Context import Common.Strategy hiding (not, fail, replicate) import qualified Common.Strategy as S import Common.Derivation+import Common.Id import Common.Navigator+import Common.TestSuite import Common.Transformation-import Common.Utils (putLabel)+import Common.Utils (ShowString(..)) import Common.View (makeView) import Control.Monad.Error-import Data.Char import Data.List import Data.Maybe+import Data.Ord import System.Random import Test.QuickCheck hiding (label) import Test.QuickCheck.Gen data Exercise a = Exercise { -- identification and meta-information- description :: String -- short sentence describing the task- , exerciseCode :: ExerciseCode -- uniquely determines the exercise (in a given domain)+ exerciseId :: Id -- identifier that uniquely determines the exercise , status :: Status -- parsing and pretty-printing , parser :: String -> Either String a@@ -70,6 +73,7 @@ , navigation :: a -> Navigator a , canBeRestarted :: Bool -- By default, assumed to be the case , extraRules :: [Rule (Context a)] -- Extra rules (possibly buggy) not appearing in strategy+ , ruleOrdering :: Rule (Context a) -> Rule (Context a) -> Ordering -- Ordering on rules (for onefirst) -- testing and exercise generation , testGenerator :: Maybe (Gen a) , randomExercise :: Maybe (StdGen -> Int -> a)@@ -77,14 +81,18 @@ } instance Eq (Exercise a) where- e1 == e2 = exerciseCode e1 == exerciseCode e2+ e1 == e2 = getId e1 == getId e2 instance Ord (Exercise a) where- e1 `compare` e2 = exerciseCode e1 `compare` exerciseCode e2+ compare = comparing getId instance Apply Exercise where applyAll ex = concatMap fromContext . applyAll (strategy ex) . inContext ex +instance HasId (Exercise a) where+ getId = exerciseId+ changeId f ex = ex { exerciseId = f (exerciseId ex) }+ testableExercise :: (Arbitrary a, Show a, Ord a) => Exercise a testableExercise = makeExercise { testGenerator = Just arbitrary@@ -100,8 +108,7 @@ emptyExercise :: Exercise a emptyExercise = Exercise { -- identification and meta-information- description = "<<description>>" - , exerciseCode = noCode+ exerciseId = error "no exercise code" , status = Experimental -- parsing and pretty-printing , parser = const (Left "<<no parser>>")@@ -118,7 +125,8 @@ , strategy = label "Fail" S.fail , navigation = noNavigator , canBeRestarted = True- , extraRules = [] + , extraRules = []+ , ruleOrdering = compareId -- testing and exercise generation , testGenerator = Nothing , randomExercise = Nothing@@ -137,28 +145,22 @@ -- returns a sorted list of rules (no duplicates) ruleset :: Exercise a -> [Rule (Context a)]-ruleset ex = nub (sortBy cmp list)+ruleset ex = nub (sortBy compareId list) where list = rulesInStrategy (strategy ex) ++ extraRules ex- cmp a b = name a `compare` name b simpleGenerator :: Gen a -> Maybe (StdGen -> Int -> a) simpleGenerator = useGenerator (const True) . const useGenerator :: (a -> Bool) -> (Int -> Gen a) -> Maybe (StdGen -> Int -> a) -useGenerator p g = Just f+useGenerator p makeGen = Just (\rng -> rec rng . makeGen) where- f rng level + rec rng gen@(MkGen f) | p a = a- | otherwise = f (snd (next rng)) level+ | otherwise = rec (snd (next rng)) gen where- a = generate 100 rng (g level)- where--generate :: Int -> StdGen -> Gen a -> a-generate n rnd (MkGen m) = m rnd' size- where- (size, rnd') = randomR (0, n) rnd+ (size, r) = randomR (0, 100) rng+ a = f r size restrictGenerator :: (a -> Bool) -> Gen a -> Gen a restrictGenerator p g = do@@ -181,6 +183,29 @@ xs !! fst (randomR (0, length xs - 1) rng) where xs = examples ex +ruleIsRecognized :: Exercise a -> Rule (Context a) -> Context a -> Context a -> Bool+ruleIsRecognized ex r ca = not . null . recognizeRule ex r ca++-- Recognize a rule at (possibly multiple) locations+recognizeRule :: Exercise a -> Rule (Context a) -> Context a -> Context a -> [Location]+recognizeRule ex r ca cb = rec (fromMaybe ca (top ca))+ where+ rec x = [ location x | here r x cb ] ++ concatMap rec (allDowns x)+ here = ruleRecognizer $ \cx cy -> fromMaybe False $+ liftM2 (similarity ex) (fromContext cx) (fromContext cy)++ruleOrderingWith :: [Rule a] -> Rule a -> Rule a -> Ordering+ruleOrderingWith = ruleOrderingWithId . map getId+ +ruleOrderingWithId :: HasId b => [b] -> Rule a -> Rule a -> Ordering+ruleOrderingWithId bs r1 r2 =+ let xs = map getId bs in+ case (findIndex (==getId r1) xs, findIndex (==getId r2) xs) of+ (Just i, Just j ) -> i `compare` j+ (Just _, Nothing) -> LT+ (Nothing, Just _ ) -> GT+ (Nothing, Nothing) -> compareId r1 r2+ --------------------------------------------------------------- -- Exercise status @@ -197,46 +222,7 @@ -- | An exercise that is not public isPrivate :: Exercise a -> Bool-isPrivate = not . isPublic-------------------------------------------------------------------- Exercise codes (unique identification)--data ExerciseCode = EC String String | NoCode- deriving (Eq, Ord)--instance Show ExerciseCode where- show (EC xs ys) = xs ++ "." ++ ys- show NoCode = "no code"--noCode :: ExerciseCode-noCode = NoCode--makeCode :: String -> String -> ExerciseCode-makeCode a b- | null a || null b || any invalidCodeChar (a++b) =- error $ "Invalid exercise code: " ++ show (EC a b)- | otherwise = - EC (map toLower a) (map toLower b)- -readCode :: String -> Maybe ExerciseCode-readCode xs =- case break invalidCodeChar xs of- (as, '.':bs) | all validCodeChar bs -> - return $ makeCode as bs- _ -> Nothing--validCodeChar, invalidCodeChar :: Char -> Bool-validCodeChar c = isAlphaNum c || c `elem` "-_"-invalidCodeChar = not . validCodeChar--domain :: ExerciseCode -> String-domain (EC s _) = s-domain _ = []--identifier :: ExerciseCode -> String-identifier (EC _ s) = s-identifier _ = []+isPrivate = not . isPublic --------------------------------------------------------------- -- Rest@@ -252,42 +238,47 @@ prettyPrinterContext ex = maybe "<<invalid term>>" (prettyPrinter ex) . fromContext -getRule :: Monad m => Exercise a -> String -> m (Rule (Context a))-getRule ex s = - case filter ((==s) . name) (ruleset ex) of +getRule :: Monad m => Exercise a -> Id -> m (Rule (Context a))+getRule ex a = + case filter ((a ==) . getId) (ruleset ex) of [hd] -> return hd- [] -> fail $ "Could not find ruleid " ++ s- _ -> fail $ "Ambiguous ruleid " ++ s+ [] -> fail $ "Could not find ruleid " ++ showId a+ _ -> fail $ "Ambiguous ruleid " ++ showId a +-- |Shows a derivation for a given start term. The specified rule ordering+-- is used for selection. showDerivation :: Exercise a -> a -> String-showDerivation ex a =- case derivation tree of- Just d -> show (f d) ++ extra d- Nothing -> prettyPrinterContext ex (root tree)- ++ "\n =>\n<<no derivation>>"+showDerivation ex a = show (present der) ++ extra where- tree = derivationTree (strategy ex) (inContext ex a)- extra d =- case fromContext (last (terms d)) of+ der = derivationDiffEnv (defaultDerivation ex a)+ extra =+ case fromContext (last (terms der)) of Nothing -> "<<invalid term>>"- Just a | isReady ex a -> ""+ Just b | isReady ex b -> "" | otherwise -> "<<not ready>>"- -- A bit of hack to show the delta between two environments, not including- -- the location variable- f d = let t:ts = map (Shown . prettyPrinterContext ex) (terms d)- xs = zipWith3 present (steps d) (drop 1 (terms d)) (terms d)- present a x y = Shown (show a ++ extra)- where env = deleteEnv "location" (diffEnv (getEnvironment x) (getEnvironment y))- extra | nullEnv env = "" - | otherwise = "\n " ++ show env- in newDerivation t (zip xs ts)+ present = mapStepsDerivation (ShowString . uncurry f) + . fmap (ShowString . prettyPrinterContext ex)+ f b env | nullEnv env = showId b+ | otherwise = showId b ++ "\n " ++ show env --- local helper datatype-data Shown = Shown String +type ExerciseDerivation a = Derivation (Rule (Context a)) (Context a) -instance Show Shown where- show (Shown s) = s+defaultDerivation :: Exercise a -> a -> ExerciseDerivation a+defaultDerivation ex a =+ let ca = inContext ex a+ tree = sortTree (ruleOrdering ex) (derivationTree (strategy ex) ca)+ single = newDerivation ca []+ in fromMaybe single (derivation tree) +derivationDiffEnv :: Derivation s (Context a) -> Derivation (s, Environment) (Context a)+derivationDiffEnv d =+ -- A bit of hack to show the delta between two environments, not including+ -- the location variable+ let t:ts = terms d+ xs = zipWith3 f (steps d) (drop 1 (terms d)) (terms d)+ f b x y = (b, deleteEnv "location" (diffEnv (getEnvironment x) (getEnvironment y))) -- ShowString (show a ++ extra)+ in newDerivation t (zip xs ts)+ printDerivation :: Exercise a -> a -> IO () printDerivation ex = putStrLn . showDerivation ex @@ -337,26 +328,28 @@ ++ " => " ++ prettyPrinterContext ex y -} checkExercise :: Exercise a -> IO ()-checkExercise ex = do- putStrLn ("** " ++ show (exerciseCode ex))+checkExercise = runTestSuite . exerciseTestSuite++exerciseTestSuite :: Exercise a -> TestSuite+exerciseTestSuite ex = suite ("Exercise " ++ show (exerciseId ex)) $ do checkExamples ex case testGenerator ex of Nothing -> return () Just gen -> do let showAsGen = showAs (prettyPrinter ex) gen- check txt p = putLabel txt >> quickCheck p- check "parser/pretty printer" $ forAll showAsGen $+ addProperty "parser/pretty printer" $ forAll showAsGen $ checkParserPrettyEx ex . from - putStrLn "Soundness non-buggy rules" - forM_ (filter (not . isBuggyRule) $ ruleset ex) $ \r -> do - putLabel (" " ++ name r)- let eq a b = equivalenceContext ex (from a) (from b)- myGen = showAs (prettyPrinterContext ex) (liftM (inContext ex) gen)- myView = makeView (return . from) (S (prettyPrinterContext ex))- testRuleSmart eq (liftRule myView r) myGen-- check "soundness strategy/generator" $ + suite "Soundness non-buggy rules" $+ forM_ (filter (not . isBuggyRule) $ ruleset ex) $ \r -> + let eq a b = equivalenceContext ex (from a) (from b)+ myGen = showAs (prettyPrinterContext ex) (liftM (inContext ex) gen)+ myView = makeView (return . from) (S (prettyPrinterContext ex))+ args = stdArgs {maxSize = 10, maxSuccess = 10, maxDiscard = 100}+ in addPropertyWith (showId r) args $ + propRuleSmart eq (liftRule myView r) myGen + + addProperty "soundness strategy/generator" $ forAll showAsGen $ maybe False (isReady ex) . fromContext . applyD (strategy ex) . inContext ex . from@@ -371,47 +364,42 @@ -- check combination of parser and pretty-printer checkParserPretty :: (a -> a -> Bool) -> (String -> Either b a) -> (a -> String) -> a -> Bool-checkParserPretty eq parser pretty a = - either (const False) (eq a) (parser (pretty a))+checkParserPretty eq p pretty a = + either (const False) (eq a) (p (pretty a)) checkParserPrettyEx :: Exercise a -> a -> Bool checkParserPrettyEx ex = checkParserPretty (similarity ex) (parser ex) (prettyPrinter ex) -checkExamples :: Exercise a -> IO ()+checkExamples :: Exercise a -> TestSuite checkExamples ex = do let xs = examples ex- unless (null xs) $ do- putStrLn $ "Checking " ++ show (length xs) ++ " examples"- bs <- forM xs $ \a -> checksForTerm True ex a- when (and bs) $ - putStrLn "Passed all tests"+ unless (null xs) $ suite "Examples" $+ mapM_ (checksForTerm True ex) xs -checksForTerm :: Bool -> Exercise a -> a -> IO Bool+checksForTerm :: Bool -> Exercise a -> a -> TestSuite checksForTerm leftMost ex a = do let tree = derivationTree (strategy ex) (inContext ex a) -- Left-most derivation- b1 <- if not leftMost then return True else- case derivation tree of- Just d -> checksForDerivation ex d- Nothing -> do - report $ "no derivation for " ++ prettyPrinter ex a- return False+ when leftMost $+ case derivation tree of+ Just d -> checksForDerivation ex d+ Nothing -> + fail $ "no derivation for " ++ prettyPrinter ex a -- Random derivation- g <- getStdGen- b2 <- case randomDerivation g tree of- Just d -> checksForDerivation ex d- Nothing -> return True - return $ and [b1, b2]+ g <- liftIO getStdGen+ case randomDerivation g tree of+ Just d -> checksForDerivation ex d+ Nothing -> return () -checksForDerivation :: Exercise a -> Derivation (Rule (Context a)) (Context a) -> IO Bool+checksForDerivation :: Exercise a -> Derivation (Rule (Context a)) (Context a) -> TestSuite checksForDerivation ex d = do -- Conditions on starting term let start = head (terms d)- b1 <- do let b = maybe False (isSuitable ex) (fromContext start)- unless b $ report $ - "start term not suitable: " ++ prettyPrinterContext ex start- return b+ assertTrueMsg "start term" + ("not suitable: " ++ prettyPrinterContext ex start) $+ maybe False (isSuitable ex) (fromContext start)+ {- b2 <- do let b = False -- maybe True (isReady ex) (fromContext start) when b $ report $ @@ -425,55 +413,35 @@ "final term is suitable: " ++ prettyPrinterContext ex start ++ " => " ++ prettyPrinterContext ex final return b -}- b4 <- do let b = maybe False (isReady ex) (fromContext final)- unless b $ report $ - "final term not ready: " ++ prettyPrinterContext ex start- ++ " => " ++ prettyPrinterContext ex final- return b+ assertTrueMsg "final term" + ("not ready: " ++ prettyPrinterContext ex start+ ++ " => " ++ prettyPrinterContext ex final) $ + maybe False (isReady ex) (fromContext final)+ -- Parser/pretty printer on terms- let ts = terms d- p = maybe False (not . checkParserPrettyEx ex) . fromContext- b5 <- case filter p ts of- [] -> return True- hd:_ -> do- let s = prettyPrinterContext ex hd - report $ "parse error for " ++ s ++ ": parsed as " - ++ either show (prettyPrinter ex) (parser ex s)- return False+ let ts = terms d+ p1 = maybe False (not . checkParserPrettyEx ex) . fromContext+ assertNull "parser/pretty-printer" $ take 1 $ flip map (filter p1 ts) $ \hd -> + let s = prettyPrinterContext ex hd + in "parse error for " ++ s ++ ": parsed as " + ++ either show (prettyPrinter ex) (parser ex s)++ -- Equivalences between terms let pairs = [ (x, y) | x <- ts, y <- ts ]- p (x, y) = not (equivalenceContext ex x y)- b6 <- case filter p pairs of- [] -> return True- (x, y):_ -> do- report $ "not equivalent: " ++ prettyPrinterContext ex x- ++ " with " ++ prettyPrinterContext ex y- return False+ p2 (x, y) = not (equivalenceContext ex x y)+ assertNull "equivalences" $ take 1 $ flip map (filter p2 pairs) $ \(x, y) ->+ "not equivalent: " ++ prettyPrinterContext ex x+ ++ " with " ++ prettyPrinterContext ex y+ -- Similarity of terms- {-- let p (x, _, y) = fromMaybe False $ + let p3 (x, _, y) = fromMaybe False $ liftM2 (similarity ex) (fromContext x) (fromContext y)- b7 <- case filter p (triples d) of- [] -> return True- (x, r, y):_ -> do- report $ "similar subsequent terms: " ++ prettyPrinterContext ex x- ++ " with " ++ prettyPrinterContext ex y- ++ " using " ++ show r- return False -}+ assertNull "similars" $ take 1 $ flip map (filter p3 (triples d)) $ \(x, r, y) -> + "similar subsequent terms: " ++ prettyPrinterContext ex x+ ++ " with " ++ prettyPrinterContext ex y+ ++ " using " ++ show r+ let xs = [ x | cx <- terms d, x <- fromContext cx, not (similarity ex x x) ]- b8 <- case xs of- [] -> return True- hd:_ -> do- report $ "term not similar to itself: " ++ prettyPrinter ex hd- return False- -- Result- return $ and [b1, b4, b5, b6, b8]--report :: String -> IO ()-report txt = putStrLn ("Error: " ++ txt)--{--generateIO :: Int -> Gen a -> IO [a]-generateIO n gen = forM [0..n] $ \i -> do- std <- newStdGen- return (generate i std gen) -}+ assertNull "self similarity" $ take 1 $ flip map xs $ \hd -> + "term not similar to itself: " ++ prettyPrinter ex hd
+ src/Common/Id.hs view
@@ -0,0 +1,163 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- Identification of entities+--+-----------------------------------------------------------------------------+module Common.Id + ( Id, IsId(..), HasId(..), ( # ), sameId+ , unqualified, qualifiers, qualification+ , describe, description, showId, compareId+ ) where++import Data.Char+import Data.List+import Data.Monoid+import Data.Ord+import Common.StringRef+import Common.Utils (splitsWithElem)++---------------------------------------------------------------------+-- Abstract data type and its instances++data Id = Id + { idList :: [String]+ , idDescription :: String+ , idRef :: !StringRef+ }+ +instance Show Id where+ show = concat . intersperse "." . idList++instance Eq Id where+ a == b = idRef a == idRef b++instance Ord Id where + compare = comparing idRef++instance Monoid Id where+ mempty = stringId ""+ mappend = ( # )++---------------------------------------------------------------------+-- Type class for constructing identifiers++class IsId a where+ newId :: a -> Id+ concatId :: [a] -> Id -- for String instance+ -- default definition+ concatId = mconcat . map newId++instance IsId Id where+ newId = id++instance IsId Char where+ newId c = stringId [c]+ concatId = stringId++instance IsId a => IsId [a] where+ newId = concatId+ concatId = mconcat . map newId++instance IsId () where+ newId = const mempty++instance (IsId a, IsId b) => IsId (a, b) where+ newId (a, b) = newId a # newId b+ +instance (IsId a, IsId b, IsId c) => IsId (a, b, c) where+ newId (a, b, c) = newId a # newId b # newId c+ +instance IsId a => IsId (Maybe a) where+ newId = maybe mempty newId+ +instance (IsId a, IsId b) => IsId (Either a b) where+ newId = either newId newId++-----------------------------------------------------+-- Type class for structures containing an identifier+ +class HasId a where+ getId :: a -> Id+ changeId :: (Id -> Id) -> a -> a+ +instance HasId Id where+ getId = id+ changeId = id++instance (HasId a, HasId b) => HasId (Either a b) where+ getId = either getId getId+ changeId f = either (Left . changeId f) (Right . changeId f)+ +---------------------------------------------------------------------+-- Private constructors++appendId :: Id -> Id -> Id+appendId a b+ | null (idList a) = b+ | null (idList b) = a+ | otherwise = Id (idList a ++ idList b) "" ref+ where+ ref = stringRef (show a ++ "." ++ show b)++-- Only allow alphanum and '-' ('.' has a special meaning)+stringId :: String -> Id+stringId txt = Id (make s) "" (stringRef s)+ where+ s = norm txt+ make = filter (not . null) . splitsWithElem '.'+ norm = filter ok . map toLower+ ok c = isAlphaNum c || c `elem` ".-_"++---------------------------------------------------------------------+-- Additional functionality (overloaded)+ +infixr 8 #++( # ) :: (IsId a, IsId b) => a -> b -> Id+a # b = appendId (newId a) (newId b)+ +sameId :: (IsId a, IsId b) => a -> b -> Bool+sameId a b = newId a == newId b+ +unqualified :: HasId a => a -> String+unqualified a+ | null xs = ""+ | otherwise = last xs+ where+ xs = idList (getId a)++qualifiers :: HasId a => a -> [String]+qualifiers a+ | null xs = []+ | otherwise = init xs+ where+ xs = idList (getId a)++qualification :: HasId a => a -> String+qualification = concat . intersperse "." . qualifiers++description :: HasId a => a -> String +description = idDescription . getId++showId :: HasId a => a -> String+showId = show . getId++compareId :: HasId a => a -> a -> Ordering+compareId = comparing showId++describe :: HasId a => String -> a -> a+describe = changeId . describeId+ where+ describeId s a+ | null (idDescription a) = + a {idDescription = s}+ | otherwise =+ a {idDescription = s ++ " " ++ idDescription a}
+ src/Common/Library.hs view
@@ -0,0 +1,54 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- Exports most from package Common+--+-----------------------------------------------------------------------------+module Common.Library + ( module Common.Classes, module Common.Transformation+ , module Common.Context, module Common.Navigator+ , module Common.Derivation+ , module Common.Rewriting, module Common.Exercise+ , module Common.Strategy, module Common.View+ , failS, notS, repeatS, replicateS, sequenceS+ ) where++import Common.Classes+import Common.Context+import Common.Derivation+import Common.Exercise+import Common.Navigator hiding (left, right)+import Common.Rewriting hiding (difference)+import Common.Strategy hiding (fail, not, repeat, replicate, sequence)+import Common.Transformation+import Common.View ++import qualified Common.Strategy as S+import Prelude (Int)++-- | Alias for strategy combinator @fail@+failS :: Strategy a+failS = S.fail++-- | Alias for strategy combinator @not@+notS :: IsStrategy f => f a -> Strategy a+notS = S.not++-- | Alias for strategy combinator @repeat@+repeatS :: IsStrategy f => f a -> Strategy a+repeatS = S.repeat++-- | Alias for strategy combinator @replicate@+replicateS :: IsStrategy f => Int -> f a -> Strategy a+replicateS = S.replicate++-- | Alias for strategy combinator @sequence@+sequenceS :: IsStrategy f => [f a] -> Strategy a+sequenceS = S.sequence
@@ -1,4 +1,4 @@-{-# OPTIONS -XExistentialQuantification #-}+{-# LANGUAGE ExistentialQuantification #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -17,13 +17,14 @@ IsNavigator(..), TypedNavigator(..) -- * Types and constructors , Navigator, Location- , navigator, noNavigator, viewNavigator+ , navigator, noNavigator, viewNavigator, viewNavigatorWith -- * Derived navigations , leave, replace, arity, isTop, isLeaf, ups, downs, navigateTo- , top, leafs, downFirst, downLast, left, right+ , navigateTowards, top, leafs, downFirst, downLast, left, right+ , replaceT ) where -import Common.Uniplate+import Common.Uniplate hiding (leafs) import Common.View hiding (left, right) import Control.Monad import Data.Maybe@@ -57,9 +58,7 @@ allDowns a = [ fa | i <- [0 .. arity a-1], fa <- down i a ] change f a =- case changeM (Just . f) a of- Just new -> new- Nothing -> a+ fromMaybe a (changeM (Just . f) a) class IsNavigator f => TypedNavigator f where changeT :: (Monad m, Typeable b) => (b -> m b) -> f a -> m (f a) @@ -67,10 +66,10 @@ leaveT :: (Monad m, Typeable b) => f a -> m b castT :: (Monad m, Typeable e) => View e b -> f a -> m (f b) -- By default, fail- changeT _ _ = fail "changeT"- currentT _ = fail "currentT"- leaveT _ = fail "leaveT"- castT _ _ = fail "castT"+ changeT _ _ = fail "changeT: not defined"+ currentT _ = fail "currentT: not defined"+ leaveT _ = fail "leaveT: not defined"+ castT _ _ = fail "castT: not defined" --------------------------------------------------------------- -- Derived navigations@@ -102,6 +101,18 @@ js = location a n = length (takeWhile id (zipWith (==) is js)) +navigateTowards :: IsNavigator f => Location -> f a -> f a+navigateTowards is a = + case ups (length js - n) a of + Just b -> safeDowns (drop n is) b+ Nothing -> a+ where + js = location a+ n = length (takeWhile id (zipWith (==) is js))+ + safeDowns [] b = b+ safeDowns (m:ms) b = maybe b (safeDowns ms) (down m b)+ top :: (IsNavigator f, Monad m) => f a -> m (f a) top = navigateTo [] @@ -144,12 +155,12 @@ -- The uniplate function is stored in the data type to get rid of the -- Uniplate type class constraints in the member functions of the -- Navigator type class.-data UniplateNav a = UN (UniplateType a) [(Int, a -> a)] a+data UniplateNav a = UN (HolesType a) [(Int, a -> a)] a -type UniplateType a = a -> ([a], [a] -> a)+type HolesType a = a -> [(a, a -> a)] -makeUN :: Uniplate a => a -> UniplateNav a-makeUN = UN uniplate []+makeUN :: HolesType a -> a -> UniplateNav a+makeUN f = UN f [] instance Show a => Show (UniplateNav a) where show = showNav@@ -158,14 +169,9 @@ up (UN _ [] _) = fail "up" up (UN uni ((_, f):xs) a) = return (UN uni xs (f a)) - allDowns (UN uni xs a) = zipWith make [0..] cs- where- (cs, build) = uni a- make i = UN uni ((i, build . flip (update i) cs):xs)- update _ _ [] = []- update i x (y:ys)- | i == 0 = x:ys- | otherwise = y:update (i-1) x ys+ allDowns (UN uni xs a) = + let make i (b, f) = UN uni ((i, f):xs) b+ in zipWith make [0..] (uni a) location (UN _ xs _) = reverse (map fst xs) @@ -203,20 +209,27 @@ leaveT (VN _ a) = leave a >>= castM castT v (VN v0 a) - | typeOf (getTp v) == typeOf (getTp v0) = - return (VN (makeView f g) a)- | otherwise = - fail "castT"+ | tp1 == tp2 = return (VN (castView v) a)+ | otherwise = fail $ "castT: " ++ show tp1 ++ " and " ++ show tp2 where- f e = castM e >>= matchM v- g = fromMaybe (error "castT") . cast . build v+ tp1 = typeOf (getTp v)+ tp2 = typeOf (getTp v0) getTp :: View a b -> a- getTp = error "castT"+ getTp = error "castT: getTp" +replaceT :: (Monad m, TypedNavigator f, Typeable b) => b -> f a -> m (f a)+replaceT = changeT . const . return+ castM :: (Monad m, Typeable a, Typeable b) => a -> m b castM = maybe (fail "castM") return . cast +castView :: (Typeable c, Typeable a) => View a b -> View c b+castView v = makeView f g+ where+ f e = castM e >>= matchM v+ g = fromMaybe (error "castT: build") . castM . build v+ --------------------------------------------------------------- -- Uniform navigator type @@ -252,10 +265,13 @@ -- Constructors navigator :: Uniplate a => a -> Navigator a-navigator = N . S . makeUN+navigator = N . S . makeUN holes noNavigator :: a -> Navigator a-noNavigator = N . S . UN (\a -> ([], const a)) []+noNavigator = N . S . UN (const []) [] viewNavigator :: (Uniplate a, Typeable a) => a -> Navigator a-viewNavigator = N . VN identity . makeUN+viewNavigator = viewNavigatorWith holes++viewNavigatorWith :: Typeable a => HolesType a -> a -> Navigator a+viewNavigatorWith f = N . VN identity . makeUN f
src/Common/Rewriting.hs view
@@ -10,15 +10,16 @@ -- ----------------------------------------------------------------------------- module Common.Rewriting - ( RewriteRule, smartGenerator, rewriteRule, rewriteRules- , Builder, rewriteM, RuleSpec((:~>)), rulePair, BuilderList, showRewriteRule- , Rewrite(..), ShallowEq(..), Operator- , associativeOperator, ruleName, Operators, collectWithOperator- , equalWith, isOperator, constructor, difference, differenceMode- , acOperator, normalizeWith, IsTerm(..), Different(..)+ ( module Common.Rewriting.Term+ , module Common.Rewriting.Group+ , module Common.Rewriting.Operator+ , module Common.Rewriting.Difference+ , module Common.Rewriting.RewriteRule ) where -import Common.Rewriting.AC import Common.Rewriting.Difference+import Common.Rewriting.Group hiding (identity)+import Common.Rewriting.Operator hiding (unary, binary, isUnary, isBinary) import Common.Rewriting.RewriteRule-import Common.Rewriting.Term+import Common.Rewriting.Term hiding (Term(..))+import Common.Rewriting.Term (Term)
src/Common/Rewriting/AC.hs view
@@ -10,123 +10,53 @@ -- ----------------------------------------------------------------------------- module Common.Rewriting.AC - ( Operator, Operators, constructor, destructor- , newOperator, associativeOperator, commutativeOperator, acOperator- , makeAssociative, makeCommutative, isAssociative, isCommutative- , collectWithOperator, buildWithOperator- , isOperator, findOperator- , normalizeWith, equalWith+ ( -- * Types+ Pairings, PairingsList, PairingsPair+ -- * Pairings with operator , pairings, pairingsMatch- , pairingsA2, onBoth+ -- * Primitive pairings functions+ , pairingsNone, pairingsA+ , pairingsC, pairingsAC ) where -import Common.Uniplate-import Common.Utils-import Data.List+import Common.View+import Common.Rewriting.Group+import Control.Monad import Data.Maybe --------------------------------------------------------------- AC theories--type Operators a = [Operator a]--data Operator a = O - { constructor :: a -> a -> a- , destructor :: a -> Maybe (a, a)- , isAssociative :: Bool- , isCommutative :: Bool- }- -newOperator :: (a -> a -> a) -> (a -> Maybe (a, a)) -> Operator a-newOperator f g = O f g False False--associativeOperator, commutativeOperator, acOperator :: (a -> a -> a) -> (a -> Maybe (a, a)) -> Operator a-associativeOperator f = makeAssociative . newOperator f-commutativeOperator f = makeCommutative . newOperator f-acOperator f = makeAssociative . commutativeOperator f--makeCommutative, makeAssociative :: Operator a -> Operator a-makeCommutative op = op { isCommutative = True }-makeAssociative op = op { isAssociative = True }--collectWithOperator :: Operator a -> a -> [a]-collectWithOperator op a- | isAssociative op = rec a []- | otherwise = maybe [a] (\(x, y) -> [x, y]) (destructor op a)- where- rec a = case destructor op a of- Just (x, y) -> rec x . rec y- Nothing -> (a:)--buildWithOperator :: Operator a -> [a] -> a-buildWithOperator op xs - | null xs = - error "Rewriting.buildWithOperator: empty list"- | not (isAssociative op) && length xs > 2 =- error "Rewriting.buildWithOperator: non-associative operator"- | otherwise = - foldr1 (constructor op) xs- -isOperator :: Operator a -> a -> Bool-isOperator op = isJust . destructor op--findOperator :: Operators a -> a -> Maybe (Operator a)-findOperator ops a = safeHead $ filter (`isOperator` a) ops--normalizeWith :: (Uniplate a, Ord a) => Operators a -> a -> a-normalizeWith ops = rec- where- rec a = - case findOperator ops a of- Just op -> - buildWithOperator op $ (if isCommutative op then sort else id) $ map rec $ collectWithOperator op a- Nothing -> - let (cs, f) = uniplate a- in f (map rec cs)--equalWith :: (Uniplate a, Ord a) => Operators a -> a -> a -> Bool-equalWith ops x y = normalizeWith ops x == normalizeWith ops y+type Pairings a = a -> a -> [[(a, a)]]+type PairingsList a b = [a] -> [b] -> [[([a], [b])]]+type PairingsPair a b = (a, a) -> (b, b) -> [[(a, b)]] ----------------------------------------------------------- -- Pairing terms with an AC theory -- matchMode: the left-hand sides cannot have the operator at top-level -pairings, pairingsMatch :: Operator a -> a -> a -> [[(a, a)]]+pairings, pairingsMatch :: IsMagma m => m a -> Pairings a pairings = pairingsMode False pairingsMatch = pairingsMode True -pairingsMode :: Bool -> Operator a -> a -> a -> [[(a, a)]]+pairingsMode :: IsMagma m => Bool -> m a -> Pairings a pairingsMode matchMode op = case (isAssociative op, isCommutative op) of- (True , True ) -> pairingsAC matchMode op- (True , False) -> pairingsA matchMode op- (False, True ) -> pairingsC op- (False, False) -> pairingsNone op+ (True , True ) -> operatorPairings op (pairingsAC matchMode)+ (True , False) -> operatorPairings op (pairingsA matchMode)+ (False, True ) -> opPairings op pairingsC+ (False, False) -> opPairings op pairingsNone -- non-associative, non-commutative pairings-pairingsNone :: Operator a -> a -> a -> [[(a, a)]]-pairingsNone op a b =- case (destructor op a, destructor op b) of- (Just (a1, a2), Just (b1, b2)) -> [[(a1, b1), (a2, b2)]]- _ -> []- +pairingsNone :: PairingsPair a b+pairingsNone (a1, a2) (b1, b2) = + [[(a1, b1), (a2, b2)]]+ -- commutative pairings-pairingsC :: Operator a -> a -> a -> [[(a, a)]]-pairingsC op a b = - case (destructor op a, destructor op b) of- (Just (a1, a2), Just (b1, b2)) -> [[(a1, b1), (a2, b2)], [(a1, b2), (a2, b1)]]- _ -> []+pairingsC :: PairingsPair a b+pairingsC (a1, a2) (b1, b2) =+ [[(a1, b1), (a2, b2)], [(a1, b2), (a2, b1)]] -- associative pairings-pairingsA :: Bool -> Operator a -> a -> a -> [[(a, a)]]-pairingsA matchMode op a b = map (map make) result- where - (as, bs) = onBoth (collectWithOperator op) (a, b)- result = pairingsA2 matchMode as bs- make = onBoth (buildWithOperator op)--pairingsA2 :: Bool -> [a] -> [a] -> [[([a], [a])]]-pairingsA2 matchMode = rec+pairingsA :: Bool -> PairingsList a b+pairingsA matchMode = rec where rec [] [] = [[]] rec as bs = @@ -142,40 +72,33 @@ ] -- associative/commutative pairings-pairingsAC :: Bool -> Operator a -> a -> a -> [[(a, a)]] -pairingsAC matchMode op a b = rec (collectWithOperator op a) (collectWithOperator op b)+pairingsAC :: Bool -> PairingsList a b+pairingsAC matchMode = rec where rec [] [] = [[]] rec [] _ = [] rec (a:as) bs = - [ (a1, b1):ps+ [ (as1, bs1):ps | (asr, as2) <- if matchMode then [([], as)] else splits as , let as1 = a:asr , (bs1, bs2) <- splits bs , not (null bs1) , length as1==1 || length bs1==1- , let a1 = buildWithOperator op as1- , let b1 = buildWithOperator op bs1 , ps <- rec as2 bs2 ] -{--data Tree = Leaf String | Bin Tree Tree deriving (Show, Eq, Ord)+----------------------------------------------------------+-- Helper functions -opBin :: Operator Tree-opBin = Operator isBin Bin- where- isBin (Bin a b) = Just (a, b)- isBin _ = Nothing- -tree1 = Bin (Bin (Leaf "1") (Leaf "2")) (Bin (Leaf "3") (Leaf "4")) -- Bin (Bin (Leaf "a") (Leaf "b")) (Bin (Leaf "c") (Leaf "d"))-tree2 = Bin (Bin (Leaf "a") (Leaf "b")) (Bin (Leaf "c") (Leaf "d")) --Bin (Bin (Leaf "w") (Leaf "x")) (Bin (Leaf "y") (Leaf "z"))+opPairings :: IsMagma m => m a -> PairingsPair a a -> Pairings a+opPairings op f a b = fromMaybe [] $+ liftM2 f (match (magmaView op) a) (match (magmaView op) b) -ex1 = pairingsC opBin tree1 tree2-ex2 = pairingsA False opBin tree1 tree2-ex3 = pairingsA True opBin tree1 tree2-ex4 = pairingsAC False opBin tree1 tree2-ex5 = pairingsAC True opBin tree1 tree2 -}+operatorPairings :: IsMagma m => m a -> PairingsList a a -> Pairings a +operatorPairings op g = curry $ + let f a = fromMaybe [a] $ match (magmaListView op) a+ h = build (magmaListView op)+ in map (map (onBoth h)) . uncurry g . onBoth f splits :: [a] -> [([a], [a])] splits = foldr insert [([], [])]
+ src/Common/Rewriting/Axioms.hs view
@@ -0,0 +1,147 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- Group axioms specified as rewrite rules (directed).+--+-----------------------------------------------------------------------------+module Common.Rewriting.Axioms + ( -- Semigroup+ leftAssociative, rightAssociative, associative+ -- Monoid+ , leftIdentity, rightIdentity+ -- Group+ , leftInverse, rightInverse+ , inverseIdentity, inverseTwice+ , flippedInverseDistribution+ , groupAxioms+ -- Abelian group+ , commutative, inverseDistribution+ ) where++import Common.Id+import Common.Rewriting.Group+import Common.Rewriting.RewriteRule++-- helper+rule :: (IsMagma m, IsId n, RuleBuilder f a, Rewrite a) => m a -> n -> f -> RewriteRule a+rule m s = rewriteRule (getId (toMagma m), s)++-------------------------------------------------------------------+-- * SemiGroup++leftAssociative :: (IsSemiGroup m, Different a, Rewrite a) => m a -> RewriteRule a+leftAssociative m = rule m "associative.left" $ + \x y z -> x.(y.z) :~> (x.y).z+ where + (.) = operation m++rightAssociative :: (IsSemiGroup m, Different a, Rewrite a) => m a -> RewriteRule a+rightAssociative m = rule m "associative.right" $ + \x y z -> (x.y).z :~> x.(y.z)+ where + (.) = operation m++associative :: (IsSemiGroup m, Different a, Rewrite a) => m a -> RewriteRule a+associative m+ | leftIsPreferred m = leftAssociative m+ | otherwise = rightAssociative m++-------------------------------------------------------------------+-- * Monoid++leftIdentity :: (IsMonoid m, Different a, Rewrite a) => m a -> RewriteRule a+leftIdentity m = rule m "identity.left" $ + \x -> e.x :~> x+ where + (.) = operation m+ e = identity m++rightIdentity :: (IsMonoid m, Different a, Rewrite a) => m a -> RewriteRule a+rightIdentity m = rule m "identity.right" $ + \x -> x.e :~> x+ where + (.) = operation m+ e = identity m++-------------------------------------------------------------------+-- * Group++leftInverse :: (IsGroup m, Different a, Rewrite a) => m a -> RewriteRule a+leftInverse m = rule m "inverse.left" $ + \x -> f x.x :~> e+ where + (.) = operation m+ e = identity m+ f = inverse m++rightInverse :: (IsGroup m, Different a, Rewrite a) => m a -> RewriteRule a+rightInverse m = rule m "inverse.right" $ + \x -> x.f x :~> e+ where + (.) = operation m+ e = identity m+ f = inverse m++inverseIdentity :: (IsGroup m, Different a, Rewrite a) => m a -> RewriteRule a+inverseIdentity m = rule m "inverse.identity" $ + f e :~> e+ where+ e = identity m+ f = inverse m++inverseTwice :: (IsGroup m, Different a, Rewrite a) => m a -> RewriteRule a+inverseTwice m = rule m "inverse.twice" $ + \x -> f (f x) :~> x+ where + f = inverse m++flippedInverseDistribution :: (IsGroup m, Different a, Rewrite a) => m a -> RewriteRule a+flippedInverseDistribution m = rule m "inverse.distribution.flipped" $ + \x y -> f (x.y) :~> f y.f x+ where + (.) = operation m+ f = inverse m++groupAxioms :: (IsGroup m, Different a, Rewrite a) => m a -> [RewriteRule a]+groupAxioms g = map ($ g)+ [ associative, leftIdentity, rightIdentity+ , leftInverse, rightInverse+ , inverseIdentity, inverseTwice, flippedInverseDistribution+ ] ++ extra+ where+ extra + | leftIsPreferred g =+ [ rule g "group1" $ \x y -> (y.x).f x :~> y+ , rule g "group2" $ \x y -> (y.f x).x :~> y+ ]+ | otherwise = + [ rule g "group3" $ \x y -> f x.(x.y) :~> y+ , rule g "group4" $ \x y -> x.(f x.y) :~> y+ ]+ (.) = operation g+ f = inverse g++-------------------------------------------------------------------+-- * Abelian Group++-- The type class constraint IsAbelianGroup could be relaxed to +-- IsCommutative (or something similar)+commutative :: (IsAbelianGroup m, Different a, Rewrite a) => m a -> RewriteRule a+commutative m = rule m "commutative" $ + \x y -> x.y :~> y.x+ where + (.) = operation m+ +inverseDistribution :: (IsAbelianGroup m, Different a, Rewrite a) => m a -> RewriteRule a+inverseDistribution m = rule m "inverse.distribution" $ + \x y -> f (x.y) :~> f x.f y+ where + (.) = operation m+ f = inverse m
+ src/Common/Rewriting/Confluence.hs view
@@ -0,0 +1,163 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Common.Rewriting.Confluence + ( isConfluent, checkConfluence, checkConfluenceWith+ , somewhereM+ , Config, defaultConfig, showTerm, complexity, termEquality+ ) where++import Common.Id+import Common.Navigator+import Common.Rewriting.RewriteRule+import Common.Rewriting.Substitution+import Common.Rewriting.Unification+import Common.Rewriting.Term+import Common.Uniplate hiding (rewriteM)+import Data.Char+import Data.Maybe++normalForm :: [RewriteRule a] -> Term -> Term+normalForm rs = run []+ where+ run hist a = + case [ b | r <- rs, b <- somewhereM (rewriteTerm r) a ] of+ [] -> a+ hd:_ -> if hd `elem` hist+ then error "cyclic"+ else run (a:hist) hd ++rewriteTerm :: RewriteRule a -> Term -> [Term]+rewriteTerm r t = do+ let lhs :~> rhs = ruleSpecTerm r+ sub <- match [] lhs t+ return (sub |-> rhs)++-- uniplate-like helper-functions+somewhereM :: Uniplate a => (a -> [a]) -> a -> [a]+somewhereM f = concatMap leave . rec . navigator+ where+ rec ca = changeM f ca ++ concatMap rec (allDowns ca)++----------------------------------------------------++type Pair a = (RewriteRule a, Term)+type Triple a = (RewriteRule a, Term, Term)++superImpose :: RewriteRule a -> RewriteRule a -> [Navigator Term]+superImpose r1 r2 = rec (navigator lhs1)+ where+ lhs1 :~> _ = ruleSpecTerm r1+ lhs2 :~> _ = ruleSpecTerm (renumber r1 r2)+ + rec ca = case current ca of+ Just (Meta _) -> []+ Just a -> [ subTop s ca | s <- unifyM a lhs2 ] ++ concatMap rec (allDowns ca)+ Nothing -> []+ + subTop s ca = fromMaybe ca $ + fmap (change (freeze . (s |->))) (top ca) >>= navigateTo (location ca)+ + renumber r = case metaInRewriteRule r of+ [] -> id+ xs -> renumberRewriteRule (maximum xs + 1)++criticalPairs :: [RewriteRule a] -> [(Term, Pair a, Pair a)]+criticalPairs rs = + [ (freeze a, (r1, freeze b1), (r2, freeze b2)) + | r1 <- rs+ , r2 <- rs+ , na <- superImpose r1 r2+ , compareId r1 r2 == LT || not (null (location na))+ , a <- leave na+ , b1 <- rewriteTerm r1 a+ , b2 <- changeM (rewriteTerm r2) na >>= leave+ ]++noDiamondPairs :: Config -> [RewriteRule a] -> [(Term, Triple a, Triple a)]+noDiamondPairs cfg rs = noDiamondPairsWith (normalForm rs) cfg rs++noDiamondPairsWith :: (Term -> Term) -> Config -> [RewriteRule a] -> [(Term, Triple a, Triple a)]+noDiamondPairsWith f cfg rs =+ [ (a, (r1, e1, nf1), (r2, e2, nf2)) + | (a, (r1, e1), (r2, e2)) <- criticalPairs rs+ , let (nf1, nf2) = (f e1, f e2)+ , not (termEquality cfg nf1 nf2)+ ]++reportPairs :: Config -> [(Term, Triple a, Triple a)] -> IO ()+reportPairs cfg = putStrLn . unlines . zipWith report [1::Int ..]+ where+ f = showTerm cfg . unfreeze+ report i (a, (r1, e1, nf1), (r2, e2, nf2)) = unlines+ [ show i ++ ") " ++ f a+ , " " ++ showId r1+ , " " ++ f e1 ++ if e1==nf1 then "" else " --> " ++ f nf1+ , " " ++ showId r2+ , " " ++ f e2 ++ if e2==nf2 then "" else " --> " ++ f nf2+ ]++freeze :: Term -> Term+freeze (Meta n) = Con (newId ('m' : show n))+freeze term = descend freeze term++unfreeze :: Term -> Term+unfreeze (Con s) = case showId s of + 'm':is | all isDigit is -> -- && not (null is) -> + Meta (read is)+ _ -> Con s+unfreeze term = descend unfreeze term+++----------------------------------------------------++isConfluent :: [RewriteRule a] -> Bool+isConfluent = null . noDiamondPairs defaultConfig++checkConfluence :: [RewriteRule a] -> IO ()+checkConfluence = checkConfluenceWith defaultConfig++checkConfluenceWith :: Config -> [RewriteRule a] -> IO ()+checkConfluenceWith cfg = reportPairs cfg . noDiamondPairs cfg++data Config = Config+ { showTerm :: Term -> String+ , complexity :: Term -> Int+ , termEquality :: Term -> Term -> Bool+ }+ +defaultConfig :: Config+defaultConfig = Config show (const 0) (==)++----------------------------------------------------+-- Example+{-+r1, r2, r3, r4, r5 :: RewriteRule SLogic+r1 = rewriteRule "R1" $ \p q r -> p :||: (q :||: r) :~> (p :||: q) :||: r +r2 = rewriteRule "R2" $ \p q -> p :||: q :~> q :||: p+r3 = rewriteRule "R3" $ \p -> p :||: p :~> p+r4 = rewriteRule "R4" $ \p -> p :||: T :~> T+r5 = rewriteRule "R5" $ \p -> p :||: F :~> p++this = [r1, r2, r3, r4, r5, r6]+go = reportPairs $ noDiamondPairs this++r6 :: RewriteRule SLogic+r6 = rewriteRule "R6" $ \p -> p :||: T :~> F ++r1, r2, r3 :: RewriteRule Expr+r1 = rewriteRule "a1" $ \a -> 0+a :~> a+r2 = rewriteRule "a3" $ \a b c -> a+(b+c) :~> (a+b)+c+r3 = rewriteRule "a2" $ \a -> a+0 :~> a++go = do -- putStrLn $ unlines $ map show $ criticalPairs [r1,r2]+ checkConfluence [r1,r2,r3]+-}
src/Common/Rewriting/Difference.hs view
@@ -16,13 +16,16 @@ ( difference, differenceEqual, differenceMode ) where -import Common.Rewriting.AC+import Common.Rewriting.Group+import Common.Rewriting.Term import Common.Rewriting.RewriteRule+import Common.View+import Common.Utils (safeHead) import Control.Monad import Common.Uniplate import Data.Maybe -differenceMode :: (Rewrite a, Uniplate a, ShallowEq a) +differenceMode :: (Rewrite a, Uniplate a) => (a -> a -> Bool) -> Bool -> a -> a -> Maybe (a, a) differenceMode eq b = if b then differenceEqual eq else difference@@ -30,51 +33,59 @@ -- | This function returns the difference, except that the -- returned terms should be logically equivalent. Nothing can signal that -- there is no difference, or that the terms to start with are not equivalent.-differenceEqual :: (Rewrite a, Uniplate a, ShallowEq a) +differenceEqual :: (Rewrite a, Uniplate a) => (a -> a -> Bool) -> a -> a -> Maybe (a, a) differenceEqual eq p q = do guard (eq p q) diff eq p q -difference :: (Rewrite a, Uniplate a, ShallowEq a) - => a -> a -> Maybe (a, a)+difference :: (Rewrite a, Uniplate a) => a -> a -> Maybe (a, a) difference = diff (\_ _ -> True) +shallowEq :: IsTerm a => a -> a -> Bool+shallowEq a b = + let f = liftM fst . getFunction+ ta = toTerm a+ tb = toTerm b + in fromMaybe (ta == tb) $ liftM2 (==) (f ta) (f tb)++findOperator :: [Magma a] -> a -> Maybe (Magma a)+findOperator ops a = safeHead $ filter (`isOperator` a) ops+ where isOperator op = isJust . match (magmaView op)+ -- local implementation function-diff :: (Rewrite a, Uniplate a, ShallowEq a) - => (a -> a -> Bool) -> a -> a -> Maybe (a, a)-diff eq p q - | shallowEq p q =- case findOperator operators p of- Just op | isAssociative op && not (isCommutative op) -> - let ps = collectWithOperator op p- qs = collectWithOperator op q- in diffA eq op ps qs- _ -> diffList eq (children p) (children q)- | otherwise = Just (p, q)+diff :: (Rewrite a, Uniplate a) => (a -> a -> Bool) -> a -> a -> Maybe (a, a)+diff eq = rec+ where+ rec p q+ | shallowEq p q =+ case findOperator operators p of+ Just op | isAssociative op && not (isCommutative op) -> do+ ps <- match (magmaListView op) p+ qs <- match (magmaListView op) q+ diffA op ps qs+ _ -> diffList (children p) (children q)+ | otherwise = Just (p, q) -diffList :: (Rewrite a, Uniplate a, ShallowEq a) - => (a -> a -> Bool) -> [a] -> [a] -> Maybe (a, a)-diffList eq xs ys- | length xs /= length ys = Nothing- | otherwise = - case catMaybes (zipWith (diff eq) xs ys) of- [p] -> Just p- _ -> Nothing+ diffList xs ys+ | length xs /= length ys = Nothing+ | otherwise = + case catMaybes (zipWith rec xs ys) of+ [p] -> Just p+ _ -> Nothing -diffA :: (Rewrite a, Uniplate a, ShallowEq a) - => (a -> a -> Bool) -> Operator a -> [a] -> [a] -> Maybe (a, a)-diffA eq op = curry (make . uncurry rev . f . uncurry rev . f)- where- f (p:ps, q:qs) | not (null ps || null qs) && - isNothing (diff eq p q) && - (equal ps qs) = - f (ps, qs)- f pair = pair- - equal ps qs = buildWithOperator op ps `eq` buildWithOperator op qs- rev ps qs = (reverse ps, reverse qs)- make pair = - case pair of - ([p], [q]) -> diff eq p q- (ps, qs) -> Just (buildWithOperator op ps, buildWithOperator op qs)+ diffA op = curry (make . uncurry rev . f . uncurry rev . f)+ where+ f (p:ps, q:qs) | not (null ps || null qs) && + isNothing (rec p q) && + equal ps qs = + f (ps, qs)+ f pair = pair+ + equal ps qs = builder ps `eq` builder qs+ rev ps qs = (reverse ps, reverse qs)+ builder = build (magmaListView op)+ make pair = + case pair of + ([p], [q]) -> rec p q+ (ps, qs) -> Just (builder ps, builder qs)
+ src/Common/Rewriting/Group.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- A hierarchy of magma's (binary operators) and groups, up to Abelian groups.+--+-----------------------------------------------------------------------------+module Common.Rewriting.Group + ( -- Magma+ IsMagma(..), Magma, magma, magmaView, magmaListView+ , withMatch, findMagma+ , isAssociative, isCommutative, isIdempotent+ , makeAssociative, makeCommutative, makeIdempotent+ -- Semigroup+ , IsSemiGroup(..), SemiGroup, semiGroup+ , leftIsPreferred, rightIsPreferred+ , preferLeft, preferRight+ -- Monoid+ , IsMonoid(..), Monoid, monoid+ -- Group+ , IsGroup(..), Group, group+ -- Abelian group+ , IsAbelianGroup(..), AbelianGroup, abelianGroup+ ) where++import Control.Arrow+import Common.Id+import Common.View hiding (identity)+import Common.Rewriting.Operator++-------------------------------------------------------------------+-- * Magma++class IsMagma f where + operation :: f a -> a -> a -> a+ hasMagma :: f a -> (Magma a, Magma a -> f a)+ toMagma :: f a -> Magma a+ changeMagma :: (Magma a -> Magma a) -> f a -> f a+ -- default definitions+ operation = binary . magmaBinaryOp . toMagma+ toMagma = fst . hasMagma+ changeMagma f = uncurry (flip ($)) . first f . hasMagma++data Magma a = Magma+ { magmaBinaryOp :: BinaryOp a+ , magmaProperties :: [MagmaProperty]+ }++data MagmaProperty = Associative | Commutative | Idempotent | PreferLeft+ deriving Eq++instance Show (Magma a) where+ show m = "Magma " ++ showId m++instance HasId (Magma a) where+ getId = getId . magmaBinaryOp+ changeId f m = m {magmaBinaryOp = changeId f (magmaBinaryOp m)}++instance IsMagma Magma where+ hasMagma a = (a, id)++magma :: BinaryOp a -> Magma a+magma op = Magma op []++magmaView :: IsMagma m => m a -> View a (a, a)+magmaView = binaryView . magmaBinaryOp . toMagma++-- The list can (and should) only contain more than two elements if the magma +-- is associative+magmaListView :: IsMagma m => m a -> View a [a]+magmaListView m = makeView (Just . toList) fromList+ where+ toList = if isAssociative m then ($ []) . rec else f+ + f a = maybe [a] (\(x, y) -> [x, y]) (match (magmaView m) a)+ + rec a = case match (magmaView m) a of+ Just (b, c) -> rec b . rec c+ Nothing -> (a:)++ fromList xs+ | null xs =+ error "semiGroupView.build: empty list"+ | n>2 && not (isAssociative m) =+ error $ "semiGroupView.build: not associativity for " + ++ showId (toMagma m)+ | otherwise = fold (operation m) xs+ where+ n = length xs+ fold = if hasProperty PreferLeft m then foldl1 else foldr1++withMatch :: IsMagma m => (a -> Maybe (a, a)) -> m a -> m a+withMatch f = changeMagma $ \m -> m {magmaBinaryOp = g (magmaBinaryOp m)}+ where+ g op = makeBinary (getId op) (binary op) f++isAssociative, isCommutative, isIdempotent :: IsMagma m => m a -> Bool+isAssociative = hasProperty Associative+isCommutative = hasProperty Commutative+isIdempotent = hasProperty Idempotent++makeAssociative, makeCommutative, makeIdempotent :: IsMagma m => m a -> m a+makeAssociative = giveProperty Associative+makeCommutative = giveProperty Commutative+makeIdempotent = giveProperty Idempotent++findMagma :: IsMagma m => (m a -> b) -> m a -> (Magma a, Magma a -> b)+findMagma f = second (f .) . hasMagma++-- helper functions+hasProperty :: IsMagma m => MagmaProperty -> m a -> Bool+hasProperty p = elem p . magmaProperties . toMagma++giveProperty :: IsMagma m => MagmaProperty -> m a -> m a+giveProperty p = changeMagma $ \m -> + m {magmaProperties = p:magmaProperties m}++removeProperty :: IsMagma m => MagmaProperty -> m a -> m a+removeProperty p = changeMagma $ \m -> + m {magmaProperties = filter (/=p) (magmaProperties m)}++-------------------------------------------------------------------+-- * SemiGroup++class IsMagma f => IsSemiGroup f where+ toSemiGroup :: f a -> SemiGroup a+ -- default definition+ toSemiGroup m = SemiGroup (rightIsPreferred m) (toMagma m)++data SemiGroup a = SemiGroup Bool (Magma a)++instance Show (SemiGroup a) where+ show m = "Semigroup " ++ showId m++instance HasId (SemiGroup a) where+ getId = getId . toMagma+ changeId = changeMagma . changeId++instance IsMagma SemiGroup where+ hasMagma (SemiGroup b m) = findMagma (SemiGroup b) m++instance IsSemiGroup SemiGroup where+ toSemiGroup = id++semiGroup :: BinaryOp a -> SemiGroup a+semiGroup op = makeAssociative $ SemiGroup True (magma op)++leftIsPreferred, rightIsPreferred :: IsSemiGroup m => m a -> Bool+leftIsPreferred = hasProperty PreferLeft+rightIsPreferred = not . leftIsPreferred++preferLeft, preferRight :: IsSemiGroup m => m a -> m a+preferLeft = giveProperty PreferLeft+preferRight = removeProperty PreferLeft++-------------------------------------------------------------------+-- * Monoid++class IsSemiGroup f => IsMonoid f where+ identity :: f a -> a+ identityCon :: f a -> Constant a+ toMonoid :: f a -> Monoid a+ -- default definition+ identity = constant . identityCon+ toMonoid m = Monoid (identityCon m) (toSemiGroup m)++data Monoid a = Monoid (Constant a) (SemiGroup a)++instance Show (Monoid a) where+ show m = "Monoid " ++ showId m++instance HasId (Monoid a) where+ getId = getId . toMagma+ changeId = changeMagma . changeId++instance IsMagma Monoid where+ hasMagma (Monoid e g) = findMagma (Monoid e) g++instance IsSemiGroup Monoid++instance IsMonoid Monoid where+ identityCon (Monoid e _) = e+ toMonoid = id++monoid :: BinaryOp a -> Constant a -> Monoid a+monoid op e = Monoid e (semiGroup op)++-------------------------------------------------------------------+-- * Group++class IsMonoid f => IsGroup f where+ inverse :: f a -> a -> a+ inverseOp :: f a -> UnaryOp a+ toGroup :: f a -> Group a+ -- default definition+ inverse = unary . inverseOp+ toGroup g = Group (inverseOp g) (toMonoid g)++data Group a = Group (UnaryOp a) (Monoid a)++instance Show (Group a) where+ show m = "Group " ++ showId m++instance HasId (Group a) where+ getId = getId . toMagma+ changeId = changeMagma . changeId++instance IsMagma Group where+ hasMagma (Group inv m) = findMagma (Group inv) m++instance IsSemiGroup Group++instance IsMonoid Group where+ identityCon (Group _ m) = identityCon m++instance IsGroup Group where+ inverseOp (Group inv _) = inv+ toGroup = id++group :: BinaryOp a -> Constant a -> UnaryOp a -> Group a+group op e inv = Group inv (monoid op e)++-------------------------------------------------------------------+-- * Abelian Group++class IsGroup f => IsAbelianGroup f where+ toAbelianGroup :: f a -> AbelianGroup a+ -- default definition+ toAbelianGroup = AbelianGroup . toGroup++newtype AbelianGroup a = AbelianGroup (Group a)+ deriving (HasId, IsMagma, IsSemiGroup, IsMonoid, IsGroup)++abelianGroup :: BinaryOp a -> Constant a -> UnaryOp a -> AbelianGroup a+abelianGroup op e inv = makeCommutative $ AbelianGroup (group op e inv)++instance Show (AbelianGroup a) where+ show m = "Abelian group " ++ showId m+ +instance IsAbelianGroup AbelianGroup
− src/Common/Rewriting/MetaVar.hs
@@ -1,76 +0,0 @@-{-# LANGUAGE TypeSynonymInstances #-}--------------------------------------------------------------------------------- Copyright 2010, 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 Common.Rewriting.MetaVar where--import Common.Uniplate-import Common.Utils (readInt)-import qualified Data.IntSet as IS----------------------------------------------------------------- Meta variables---- | Type class for creating meta-variables-class MetaVar a where- metaVar :: Int -> a- isMetaVar :: a -> Maybe Int--instance MetaVar String where- isMetaVar ('_':xs) = readInt xs- isMetaVar _ = Nothing- metaVar n = '_' : show n- --- | Produces an infinite list of meta-variables-metaVars :: MetaVar a => [a]-metaVars = map metaVar [0..]---- | Collect all meta-variables-getMetaVars :: (MetaVar a, Uniplate a) => a -> IS.IntSet-getMetaVars a = getMetaVarsList [a]---- | Collect all meta-variables in the list-getMetaVarsList :: (MetaVar a, Uniplate a) => [a] -> IS.IntSet-getMetaVarsList xs = IS.fromList [ i | x <- xs, a <- universe x, Just i <- [isMetaVar a] ]---- | Checks whether the meta-variable is used in a term-hasMetaVar :: (MetaVar a, Uniplate a) => Int -> a -> Bool-hasMetaVar i = IS.member i . getMetaVars---- | Checks whether the meta-variable is used in one of the elements in the list-hasMetaVarList :: (MetaVar a, Uniplate a) => Int -> [a] -> Bool-hasMetaVarList i = IS.member i . getMetaVarsList---- | Checks whether a value has no variables-noMetaVars :: (Uniplate a, MetaVar a) => a -> Bool-noMetaVars = IS.null . getMetaVars ---- | Determine what the next unused meta-varable is-nextMetaVar :: (Uniplate a, MetaVar a) => a -> Int-nextMetaVar a = nextMetaVarOfList [a]---- | Determine what the next meta-variable is that is not used in--- an element of the list-nextMetaVarOfList :: (Uniplate a, MetaVar a) => [a] -> Int-nextMetaVarOfList xs- | IS.null s = 0- | otherwise = 1 + IS.findMax s- where- s = getMetaVarsList xs---- | Rename the meta-variables -renameMetaVars :: (MetaVar a, Uniplate a) => (Int -> Int) -> a -> a-renameMetaVars f a =- case isMetaVar a of- Just i -> metaVar (f i)- Nothing -> g $ map (renameMetaVars f) cs- where - (cs, g) = uniplate a
+ src/Common/Rewriting/Operator.hs view
@@ -0,0 +1,117 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Common.Rewriting.Operator+ ( -- * Constants+ Constant, makeConstant, simpleConstant+ , constant, isConstant, constantView+ -- * Unary operators+ , UnaryOp, makeUnary, simpleUnary+ , unary, isUnary, unaryMatch, unaryView+ -- * Binary operators+ , BinaryOp, makeBinary, simpleBinary+ , binary, isBinary, binaryMatch, binaryView+ ) where++import Common.Id+import Common.Uniplate+import Common.View+import Data.Maybe+import Control.Monad++----------------------------------------------------------------------+-- Constants++data Constant a = C+ { constantId :: Id+ , constant :: a+ , isConstant :: a -> Bool+ }++instance Show (Constant a) where+ show = showId++instance HasId (Constant a) where+ getId = constantId+ changeId f op = op {constantId = f (constantId op)}++makeConstant :: IsId n => n -> a -> (a -> Bool) -> Constant a+makeConstant = C . newId++simpleConstant :: (IsId n, Eq a) => n -> a -> Constant a+simpleConstant n a = makeConstant n a (==a)++constantView :: Constant a -> View a ()+constantView (C i a p) = newView i (guard . p) (const a)++----------------------------------------------------------------------+-- Unary operators++data UnaryOp a = U+ { unaryId :: Id+ , unary :: a -> a+ , unaryMatch :: a -> Maybe a+ }++instance Show (UnaryOp a) where+ show = showId++instance HasId (UnaryOp a) where+ getId = unaryId+ changeId f op = op {unaryId = f (unaryId op)}++makeUnary :: IsId n => n -> (a -> a) -> (a -> Maybe a) -> UnaryOp a+makeUnary = U . newId++simpleUnary :: (IsId n, Uniplate a, Eq a) => n -> (a -> a) -> UnaryOp a+simpleUnary n op = makeUnary n op f+ where+ f a = case children a of+ [x] | op x == a -> Just x+ _ -> Nothing++isUnary :: UnaryOp a -> a -> Bool+isUnary op = isJust . unaryMatch op++unaryView :: UnaryOp a -> View a a+unaryView (U i op m) = newView i m op++----------------------------------------------------------------------+-- Binary operators++data BinaryOp a = B + { binaryId :: Id+ , binary :: a -> a -> a+ , binaryMatch :: a -> Maybe (a, a)+ }++instance Show (BinaryOp a) where+ show = showId++instance HasId (BinaryOp a) where+ getId = binaryId+ changeId f op = op {binaryId = f (binaryId op)}++makeBinary :: IsId n => n -> (a -> a -> a) -> (a -> Maybe (a, a)) -> BinaryOp a+makeBinary = B . newId++simpleBinary :: (IsId n, Uniplate a, Eq a) => n -> (a -> a -> a) -> BinaryOp a+simpleBinary n op = makeBinary n op f+ where+ f a = case children a of+ [x, y] | op x y == a -> Just (x, y)+ _ -> Nothing++isBinary :: BinaryOp a -> a -> Bool+isBinary op = isJust . binaryMatch op++binaryView :: BinaryOp a -> View a (a, a)+binaryView (B n op m) = newView n m (uncurry op)
src/Common/Rewriting/RewriteRule.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE ExistentialQuantification, MultiParamTypeClasses, - FunctionalDependencies, FlexibleInstances, UndecidableInstances,- TypeSynonymInstances #-}+ FunctionalDependencies, FlexibleInstances, UndecidableInstances #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -14,45 +13,42 @@ ----------------------------------------------------------------------------- module Common.Rewriting.RewriteRule ( -- * Supporting type classes- Rewrite(..), ShallowEq(..), Different(..)+ Rewrite(..), Different(..) -- * Rewrite rules and specs- , RewriteRule(ruleName, rulePair), RuleSpec(..)- -- * Compiling a rewrite rule- , rewriteRule, rewriteRules, Builder, BuilderList+ , RewriteRule, ruleSpecTerm, RuleSpec(..)+ -- * Compiling rewrite rules+ , rewriteRule, RuleBuilder -- * Using rewrite rules , rewrite, rewriteM, showRewriteRule, smartGenerator+ , metaInRewriteRule, renumberRewriteRule, inverseRule+ , useOperators ) where -import Common.Rewriting.AC+import Common.Classes+import Common.Id+import Common.View hiding (match) import Common.Rewriting.Substitution import Common.Rewriting.Term+import Common.Rewriting.Group+import Common.Rewriting.Unification hiding (match)+import Common.Uniplate (descend, leafs) import Control.Monad+import Data.Maybe import Test.QuickCheck-import Common.Apply-import Common.Rewriting.MetaVar (getMetaVars)-import Common.Rewriting.Unification+import qualified Common.Rewriting.Unification as Unification import qualified Data.IntSet as IS-import qualified Data.Map as M-+ ------------------------------------------------------ -- Supporting type classes -class Different a where- different :: (a, a)--class ShallowEq a where - shallowEq :: a -> a -> Bool- -- The arbitrary type class is a quick solution to have smart generators -- (in combination with lifting rules). The function in the RewriteRule module -- cannot have a type class for this reason -- The show type class is added for pretty-printing rules class (IsTerm a, Arbitrary a, Show a) => Rewrite a where- operators :: [Operator a]- associativeOps :: a -> [Symbol]- -- default definition: no associative/commutative operators- operators = []- associativeOps = const []+ operators :: [Magma a]+ -- default definition: no special operators+ operators = [] ------------------------------------------------------ -- Rewrite rules and specs@@ -61,80 +57,69 @@ data RuleSpec a = a :~> a deriving Show -data RewriteRule a = Rewrite a => R - { ruleName :: String- , nrOfMetaVars :: Int- , rulePair :: Int -> RuleSpec Term - }- instance Functor RuleSpec where fmap f (a :~> b) = f a :~> f b ---------------------------------------------------------- Compiling a rewrite rule+instance Crush RuleSpec where+ crush (a :~> b) = [a, b] -class Builder t a | t -> a where- buildSpec :: t -> Int -> RuleSpec Term- countVars :: t -> Int+instance Zip RuleSpec where + fzipWith f (a :~> b) (c :~> d) = f a c :~> f b d -instance IsTerm a => Builder (RewriteRule a) a where- buildSpec = rulePair- countVars = nrOfMetaVars+data RewriteRule a = R+ { ruleId :: Id+ , ruleSpecTerm :: RuleSpec Term+ , ruleOperators :: [Magma a]+ , ruleShow :: a -> String+ , ruleTermView :: View Term a+ , ruleGenerator :: Gen a+ }+ +instance Show (RewriteRule a) where+ show = showId -instance IsTerm a => Builder (RuleSpec a) a where- buildSpec (a :~> b) _ = toTerm a :~> toTerm b- countVars _ = 0+instance HasId (RewriteRule a) where+ getId = ruleId+ changeId f r = r {ruleId = f (ruleId r)} -instance (Different a, Builder t b) => Builder (a -> t) b where- buildSpec f i = buildFunction i (\a -> buildSpec (f a) (i+1))- countVars f = countVars (f $ error "countVars") + 1+------------------------------------------------------+-- Compiling a rewrite rule -class BuilderList t a | t -> a where- getSpecNr :: t -> Int -> Int -> RuleSpec Term- countSpecsL :: t -> Int- countVarsL :: t -> Int+class Different a where+ different :: (a, a) -instance Rewrite a => BuilderList (RewriteRule a) a where- getSpecNr r n = if n==0 then rulePair r else error "getSpecNr"- countSpecsL _ = 1- countVarsL = nrOfMetaVars- -instance Builder t a => BuilderList [t] a where- getSpecNr rs = buildSpec . (rs !!)- countSpecsL = length- countVarsL _ = 0+class RuleBuilder t a | t -> a where+ buildRuleSpec :: t -> Int -> RuleSpec Term -instance (Different a, BuilderList t b) => BuilderList (a -> t) b where - getSpecNr f n i = buildFunction i (\a -> getSpecNr (f a) n (i+1))- countSpecsL f = countSpecsL (f $ error "countSpecsL")- countVarsL f = countVarsL (f $ error "countSpecsL") + 1+instance IsTerm a => RuleBuilder (RuleSpec a) a where+ buildRuleSpec = const . fmap toTerm -buildFunction :: Different a => Int -> (a -> RuleSpec Term) -> RuleSpec Term-buildFunction n f = fill n a1 a2 :~> fill n b1 b2- where- a1 :~> b1 = f (fst different)- a2 :~> b2 = f (snd different)+instance (Different a, RuleBuilder t b) => RuleBuilder (a -> t) b where+ buildRuleSpec f i = buildFunction i (\a -> buildRuleSpec (f a) (i+1)) +buildFunction :: (Zip f, Different a) => Int -> (a -> f Term) -> f Term+buildFunction n f = fzipWith (fill n) (f a) (f b)+ where (a, b) = different+ fill :: Int -> Term -> Term -> Term-fill i (App a1 a2) (App b1 b2) = App (fill i a1 b1) (fill i a2 b2)-fill i a b - | a == b = a- | otherwise = Meta i--build :: Rewrite a => RuleSpec Term -> a -> [a]-build (lhs :~> rhs) a = do- s <- match (getMatcher a) lhs (toTerm a)- fromTermM (s |-> rhs)--rewriteRule :: (Builder f a, Rewrite a) => String -> f -> RewriteRule a-rewriteRule s f = R s (countVars f) (buildSpec f)--rewriteRules :: (BuilderList f a, Rewrite a) => String -> f -> [RewriteRule a]-rewriteRules s f = map (R s (countVarsL f) . getSpecNr f) [0 .. countSpecsL f-1]+fill i = rec+ where+ rec (Apply f a) (Apply g b) = Apply (rec f g) (rec a b)+ rec a b + | a == b = a+ | otherwise = Meta i -getMatcher :: Rewrite a => a -> Matcher-getMatcher = M.unions . map associativeMatcher . associativeOps+buildSpec :: [Symbol] -> RuleSpec Term -> Term -> [Term]+buildSpec ops (lhs :~> rhs) a = do+ s <- Unification.match ops lhs a+ let (b1, b2) = (specialLeft `elem` dom s, specialRight `elem` dom s)+ sym = maybe (error "buildSpec") fst (getFunction lhs)+ extLeft x = if b1 then binary sym (Meta specialLeft) x else x+ extRight x = if b2 then binary sym x (Meta specialRight) else x+ return (s |-> extLeft (extRight rhs)) +rewriteRule :: (IsId n, RuleBuilder f a, Rewrite a) => n -> f -> RewriteRule a+rewriteRule s f = R (newId s) (buildRuleSpec f 0) operators show termView arbitrary ------------------------------------------------------ -- Using a rewrite rule@@ -143,10 +128,17 @@ applyAll = rewrite rewrite :: RewriteRule a -> a -> [a]-rewrite r@(R _ _ _) a = do- ext <- extendContext (associativeOps a) r- build (rulePair ext 0) a+rewrite r a = + let term = toTermRR r a+ syms = mapMaybe (operatorSymbol r a) (ruleOperators r)+ in concatMap (fromTermRR r) (buildSpec syms (ruleSpecTerm r) term) +operatorSymbol :: IsMagma m => RewriteRule a -> a -> m a -> Maybe Symbol+operatorSymbol r a op = + case getFunction (toTermRR r (operation op a a)) of+ Just (s, [_, _]) -> Just s+ _ -> Nothing+ rewriteM :: MonadPlus m => RewriteRule a -> a -> m a rewriteM r = msum . map return . rewrite r @@ -154,58 +146,51 @@ -- Pretty-print a rewriteRule showRewriteRule :: Bool -> RewriteRule a -> Maybe String-showRewriteRule sound r@(R _ _ _) = do- x <- fromTermTp r (sub |-> a)- y <- fromTermTp r (sub |-> b)+showRewriteRule sound r = do+ x <- fromTermRR r (sub |-> a)+ y <- fromTermRR r (sub |-> b) let op = if sound then "~>" else "/~>" - return (show x ++ " " ++ op ++ " " ++ show y)+ return (ruleShow r x ++ " " ++ op ++ " " ++ ruleShow r y) where- a :~> b = rulePair r 0- vs = IS.toList (getMetaVars a `IS.union` getMetaVars b)+ a :~> b = ruleSpecTerm r+ vs = IS.toList (metaVarSet a `IS.union` metaVarSet b) sub = listToSubst $ zip vs [ Var [c] | c <- ['a' ..] ]- - fromTermTp :: IsTerm a => RewriteRule a -> Term -> Maybe a- fromTermTp _ = fromTerm ----------------------------------------------------------- -- Smart generator that creates instantiations of the left-hand side smartGenerator :: RewriteRule a -> Gen a-smartGenerator r@(R _ _ _) = do - let a :~> _ = rulePair r 0- let vs = IS.toList (getMetaVars a)- list <- vector (length vs) - let sub = listToSubst (zip vs (map (tpToTerm r) list))- case fromTerm (sub |-> a) of- Just a -> return a- Nothing -> arbitrary- where- tpToTerm :: IsTerm a => RewriteRule a -> a -> Term- tpToTerm _ = toTerm+smartGenerator r = do + let a :~> _ = ruleSpecTerm r+ let vs = IS.toList (metaVarSet a)+ list <- replicateM (length vs) (ruleGenerator r)+ let sub = listToSubst (zip vs (map (toTermRR r) list))+ case fromTermRR r (sub |-> a) of+ Just x -> return x+ Nothing -> ruleGenerator r ------------------------------------------------------ --- Bug fix 4/3/2009: for associative operators, we need to extend rewrite--- rules to take "contexts" into account. In addition to a left and a right--- context, we also should consider a context on both sides. If not, we --- might miss some locations, as pointed out by Josje's bug report.-extendContext :: [Symbol] -> RewriteRule a -> [RewriteRule a]-extendContext ops r@(R _ _ _) =- case getSpine (lhs $ rulePair r 0) of- (Con s, [_, _]) | s `elem` ops -> r :- [ extend (leftContext s) r- , extend (rightContext s) r - , extend (rightContext s) (extend (leftContext s) r) - ]- _ -> [r]+inverseRule :: RewriteRule a -> RewriteRule a+inverseRule r = r {ruleSpecTerm = b :~> a}+ where a :~> b = ruleSpecTerm r++useOperators :: [Magma a] -> RewriteRule a -> RewriteRule a+useOperators xs r = r {ruleOperators = xs ++ ruleOperators r}++-- some helpers+metaInRewriteRule :: RewriteRule a -> [Int]+metaInRewriteRule r =+ [ n | a <- crush (ruleSpecTerm r), Meta n <- leafs a ]++renumberRewriteRule :: Int -> RewriteRule a -> RewriteRule a+renumberRewriteRule n r = r {ruleSpecTerm = fmap f (ruleSpecTerm r)} where- lhs (a :~> _) = a- - leftContext s a (x :~> y) =- binary s a x :~> binary s a y+ f (Meta i) = Meta (i+n)+ f term = descend f term - rightContext s a (x :~> y) =- binary s x a :~> binary s y a+toTermRR :: RewriteRule a -> a -> Term+toTermRR = build . ruleTermView -extend :: (Term -> RuleSpec Term -> RuleSpec Term) -> RewriteRule a -> RewriteRule a-extend f (R s n g) = R s (n+1) (\i -> f (Meta (i+n)) (g i))+fromTermRR :: Monad m => RewriteRule a -> Term -> m a+fromTermRR = matchM . ruleTermView
src/Common/Rewriting/Substitution.hs view
@@ -8,75 +8,66 @@ -- Stability : provisional -- Portability : portable (depends on ghc) ----- Substitutions+-- Substitutions on terms -- ----------------------------------------------------------------------------- module Common.Rewriting.Substitution - ( Substitution, emptySubst, singletonSubst, listToSubst, (@@), (@@@)- , lookupVar, dom, removeDom, ran, (|->)+ ( Substitution, emptySubst, singletonSubst, dom+ , (@@), (@@@), (|->), listToSubst ) where import Common.Uniplate-import Common.Rewriting.MetaVar+import Common.Rewriting.Term import qualified Data.IntMap as IM-import qualified Data.IntSet as IS import Data.Maybe ----------------------------------------------------------- --- Substitution -- | Abstract data type for substitutions-newtype Substitution a = S { unS :: IM.IntMap a }+newtype Substitution = S { unS :: IM.IntMap Term } infixr 4 |-> infixr 5 @@, @@@ -instance Show a => Show (Substitution a) where+instance Show Substitution where show = show . unS -- | Returns the empty substitution-emptySubst :: (Uniplate a, MetaVar a) => Substitution a+emptySubst :: Substitution emptySubst = S IM.empty -- | Returns a singleton substitution-singletonSubst :: (MetaVar a, Uniplate a) => Int -> a -> Substitution a+singletonSubst :: Int -> Term -> Substitution singletonSubst i a = S (IM.singleton i a) -- | Turns a list into a substitution-listToSubst :: (Uniplate a, MetaVar a) => [(Int, a)] -> Substitution a+listToSubst :: [(Int, Term)] -> Substitution listToSubst = S . IM.fromListWith (error "Substitution: keys are not unique") -- | Combines two substitutions. The left-hand side substitution is first applied to -- the co-domain of the right-hand side substitution-(@@) :: (Uniplate a, MetaVar a) => Substitution a -> Substitution a -> Substitution a+(@@) :: Substitution -> Substitution -> Substitution S a @@ S b = S $ a `IM.union` IM.map (S a |->) b -- | Combines two substitutions with disjoint domains. If the domains are not disjoint, -- an error is reported-(@@@) :: (Uniplate a, MetaVar a) => Substitution a -> Substitution a -> Substitution a+(@@@) :: Substitution -> Substitution -> Substitution S a @@@ S b = S (IM.unionWith err a b) where err _ _ = error "Unification.(@@@): domains of substitutions are not disjoint" -- | Lookups a variable in a substitution. Nothing indicates that the variable is -- not in the domain of the substitution-lookupVar :: Int -> Substitution a -> Maybe a+lookupVar :: Int -> Substitution -> Maybe Term lookupVar s = IM.lookup s . unS -- | Returns the domain of a substitution (as a list)-dom :: Substitution a -> IS.IntSet-dom = IM.keysSet . unS---- | Removes variables from the domain of a substitution-removeDom :: IS.IntSet -> Substitution a -> Substitution a-removeDom s (S a) = S (IM.filterWithKey (\k _ -> IS.member k s) a)--ran :: Substitution a -> [a]-ran = IM.elems . unS+dom :: Substitution -> [Int]+dom = IM.keys . unS -- | Apply the substitution-(|->) :: (MetaVar a, Uniplate a) => Substitution a -> a -> a-s |-> e = - case isMetaVar e of- Just i -> fromMaybe e (lookupVar i s)- Nothing -> let (cs, f) = uniplate e- in f (map (s |->) cs)+(|->) :: Substitution -> Term -> Term+s |-> term = + case term of+ Meta i -> fromMaybe term (lookupVar i s)+ _ -> descend (s |->) term
src/Common/Rewriting/Term.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances, DeriveDataTypeable #-}+{-# LANGUAGE DeriveDataTypeable #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -12,46 +12,71 @@ -- A simple data type for term rewriting -- ------------------------------------------------------------------------------module Common.Rewriting.Term where+module Common.Rewriting.Term + ( Term(..), IsTerm(..)+ , Symbol, newSymbol+ , fromTermM, fromTermWith+ , getSpine, makeTerm+ -- * Functions and symbols+ , WithFunctions(..), isSymbol, isFunction+ , unary, binary, isUnary, isBinary+ -- * Variables+ , WithVars(..), isVariable+ , vars, varSet, hasVar, withoutVar, hasSomeVar, hasNoVar+ -- * Meta variables+ , WithMetaVars(..), isMetaVar+ , metaVars, metaVarSet, hasMetaVar+ ) where +import Common.Id import Common.Utils (ShowString(..)) import Common.Uniplate+import Common.View import Control.Monad-import Common.Rewriting.MetaVar+import Data.Maybe import Data.Typeable+import qualified Data.IntSet as IS+import qualified Data.Set as S ----------------------------------------------------------- -- * Data type for terms -data Symbol = S (Maybe String) String- deriving (Eq, Ord)- data Term = Var String | Con Symbol - | App Term Term+ | Apply Term Term | Num Integer | Float Double | Meta Int deriving (Show, Eq, Ord, Typeable)+ +instance Uniplate Term where+ uniplate (Apply f a) = ([f, a], \[g, b] -> Apply g b)+ uniplate term = ([], \_ -> term) +newtype Symbol = S Id+ deriving (Eq, Ord)+ instance Show Symbol where- show (S ma b) = maybe b (\a -> a++ "." ++ b) ma+ show = showId -instance MetaVar Term where - metaVar = Meta- isMetaVar (Meta n) = Just n- isMetaVar _ = Nothing- -instance Uniplate Term where- uniplate (App f a) = ([f,a], \[g,b] -> App g b)- uniplate term = ([], \_ -> term)+instance HasId Symbol where+ getId (S a) = a+ changeId f (S a) = S (f a) +newSymbol :: IsId a => a -> Symbol+newSymbol = S . newId+ ----------------------------------------------------------- -- * Type class for conversion to/from terms class IsTerm a where toTerm :: a -> Term fromTerm :: MonadPlus m => Term -> m a+ termView :: View Term a+ -- default definitions+ toTerm = build termView+ fromTerm = matchM termView+ termView = makeView fromTerm toTerm instance IsTerm Term where toTerm = id@@ -72,51 +97,127 @@ fromTermM = maybe (fail "fromTermM") return . fromTerm fromTermWith :: (Monad m, IsTerm a) => (Symbol -> [a] -> m a) -> Term -> m a-fromTermWith f a = - case getSpine a of - (t, xs) -> isCon t >>= \s -> mapM fromTermM xs >>= f s+fromTermWith f a = do+ (s, xs) <- getFunction a+ ys <- mapM fromTermM xs+ f s ys -------------------------------------------------------------- * Utility functions+-- * Functions and symbols -getSpine :: Term -> (Term, [Term])-getSpine = rec []- where- rec xs (App f a) = rec (a:xs) f- rec xs a = (a, xs)+class WithFunctions a where+ -- constructing+ symbol :: Symbol -> a+ function :: Symbol -> [a] -> a+ -- matching+ getSymbol :: Monad m => a -> m Symbol+ getFunction :: Monad m => a -> m (Symbol, [a])+ -- default definition+ symbol s = function s []+ getSymbol a = + case getFunction a of+ Just (t, []) -> return t+ _ -> fail "Common.Term.getSymbol"+ +instance WithFunctions Term where+ function = makeTerm . Con+ getFunction a = + case getSpine a of+ (Con s, xs) -> return (s, xs)+ _ -> fail "Common.Rewriting.getFunction" + +isSymbol :: WithFunctions a => Symbol -> a -> Bool+isSymbol s = maybe False (==s) . getSymbol -getConSpine :: Monad m => Term -> m (Symbol, [Term])-getConSpine a = liftM (\s -> (s, xs)) (isCon b)- where (b, xs) = getSpine a+isFunction :: (WithFunctions a, Monad m) => Symbol -> a -> m [a]+isFunction s a =+ case getFunction a of+ Just (t, as) | s == t -> return as+ _ -> fail "Common.Term.isFunction" -makeTerm :: Term -> [Term] -> Term-makeTerm = foldl App+unary :: WithFunctions a => Symbol -> a -> a+unary s a = function s [a] -makeConTerm :: Symbol -> [Term] -> Term-makeConTerm = makeTerm . Con+binary :: WithFunctions a => Symbol -> a -> a -> a+binary s a b = function s [a, b] -unary :: Symbol -> Term -> Term-unary = App . Con+isUnary :: (WithFunctions a, Monad m) => Symbol -> a -> m a+isUnary s a = + case isFunction s a of+ Just [x] -> return x+ _ -> fail "Common.Term.isUnary" -binary :: Symbol -> Term -> Term -> Term-binary s = App . App (Con s)+isBinary :: (WithFunctions a, Monad m) => Symbol -> a -> m (a, a)+isBinary s a = + case isFunction s a of+ Just [x, y] -> return (x, y)+ _ -> fail "Common.Term.isBinary" -isUnary :: Symbol -> Term -> Maybe Term-isUnary s term =- case getSpine term of- (t, [a]) | isCon t == Just s -> Just a- _ -> Nothing+-----------------------------------------------------------+-- * Variables -isBinary :: Symbol -> Term -> Maybe (Term, Term)-isBinary s term =- case getSpine term of- (t, [a, b]) | isCon t == Just s -> Just (a, b)- _ -> Nothing+class WithVars a where+ variable :: String -> a+ getVariable :: Monad m => a -> m String -isVar :: Monad m => Term -> m String-isVar (Var s) = return s-isVar _ = fail "isVar"+instance WithVars Term where + variable = Var+ getVariable (Var s) = return s+ getVariable _ = fail "Common.Rewriting.getVariable" -isCon :: Monad m => Term -> m Symbol-isCon (Con s) = return s-isCon _ = fail "isCon"+isVariable :: WithVars a => a -> Bool+isVariable = isJust . getVariable++vars :: (Uniplate a, WithVars a) => a -> [String]+vars = concatMap getVariable . leafs++varSet :: (Uniplate a, WithVars a) => a -> S.Set String+varSet = S.fromList . vars++hasVar :: (Uniplate a, WithVars a) => String -> a -> Bool+hasVar i = (i `elem`) . vars++withoutVar :: (Uniplate a, WithVars a) => String -> a -> Bool+withoutVar i = not . hasVar i++hasSomeVar :: (Uniplate a, WithVars a) => a -> Bool+hasSomeVar = not . hasNoVar++hasNoVar :: (Uniplate a, WithVars a) => a -> Bool+hasNoVar = null . vars++-----------------------------------------------------------+-- * Meta variables++class WithMetaVars a where+ metaVar :: Int -> a+ getMetaVar :: Monad m => a -> m Int ++instance WithMetaVars Term where+ metaVar = Meta+ getMetaVar (Meta i) = return i+ getMetaVar _ = fail "Common.Rewriting.getMetaVar"++isMetaVar :: WithMetaVars a => a -> Bool+isMetaVar = isJust . getMetaVar++metaVars :: (Uniplate a, WithMetaVars a) => a -> [Int]+metaVars = concatMap getMetaVar . leafs++metaVarSet :: (Uniplate a, WithMetaVars a) => a -> IS.IntSet+metaVarSet = IS.fromList . metaVars++hasMetaVar :: (Uniplate a, WithMetaVars a) => Int -> a -> Bool+hasMetaVar i = (i `elem`) . metaVars++-----------------------------------------------------------+-- * Utility functions++getSpine :: Term -> (Term, [Term])+getSpine = rec [] + where+ rec xs (Apply f a) = rec (a:xs) f+ rec xs a = (a, xs)++makeTerm :: Term -> [Term] -> Term+makeTerm = foldl Apply
src/Common/Rewriting/Unification.hs view
@@ -9,18 +9,35 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Common.Rewriting.Unification (match, Matcher, associativeMatcher) where+module Common.Rewriting.Unification + ( match, unifyM, specialLeft, specialRight+ ) where import Common.Rewriting.Term import Common.Rewriting.AC-import Common.Rewriting.MetaVar import Common.Rewriting.Substitution import Control.Monad-import qualified Data.IntSet as IS-import qualified Data.Map as M ----------------------------------------------------------- -- Unification (in both ways)++unifyM :: Monad m => Term -> Term -> m Substitution+unifyM term1 term2 = + case (term1, term2) of+ (Meta i, Meta j) | i == j -> + return emptySubst+ (Meta i, _) | not (i `hasMetaVar` term2) -> + return (singletonSubst i term2)+ (_, Meta j) | not (j `hasMetaVar` term1) -> + return (singletonSubst j term1)+ (Apply f a, Apply g b) -> do+ s1 <- unifyM f g+ s2 <- unifyM (s1 |-> a) (s1 |-> b)+ return (s1 @@ s2)+ _ | term1 == term2 -> + return emptySubst+ _ -> fail "unifyM: no unifier"+ {- class ShallowEq a where shallowEq :: a -> a -> Bool@@ -66,51 +83,56 @@ ----------------------------------------------------------- -- Matching (or: one-way unification) -match :: Matcher -> Term -> Term -> [Substitution Term]-match m x y = do- s <- rec x y- guard (IS.null $ dom s `IS.intersection` getMetaVars y)- return s+-- second term should not have meta variables++match :: [Symbol] -> Term -> Term -> [Substitution]+match assocSymbols = rec True where- rec (Meta i) y = do - guard (not (hasMetaVar i y))+ rec _ (Meta i) y = return (singletonSubst i y) - rec x y = do- let (a, as) = getSpine x- (b, bs) = getSpine y- case isCon a >>= (`M.lookup` m) of- Just f -> - concatMap (uncurry recList . unzip) (f x y)- Nothing -> do+ rec isTop x y =+ case getSpine x of+ (Con s, [a1, a2]) | s `elem` assocSymbols ->+ concatMap (uncurry recList . unzip) (associativeMatch isTop s a1 a2 y)+ (a, as) -> do+ let (b, bs) = getSpine y guard (a == b) recList as bs recList [] [] = return emptySubst recList (x:xs) (y:ys) = do- s1 <- rec x y+ s1 <- rec False x y s2 <- recList (map (s1 |->) xs) (map (s1 |->) ys)- return (s2 @@ s1)+ return (s2 @@@ s1) recList _ _ = []--type Matcher = M.Map Symbol (Term -> Term -> [[(Term, Term)]])--associativeMatcher :: Symbol -> Matcher-associativeMatcher s = M.singleton s f+ +associativeMatch :: Bool -> Symbol -> Term -> Term -> Term -> [[(Term, Term)]]+associativeMatch isTop s1 a1 a2 (Apply (Apply (Con s2) b1) b2) + | s1==s2 = map (map make) result where- f a b = map (map make) result- where- (as, bs) = onBoth collect (a, b)- result = pairingsA2 True as bs- make = onBoth construct+ as = collect a1 . collect a2 $ []+ bs = collect b1 . collect b2 $ []+ list | isTop = map ($ as) [id, extLeft, extRight, extBoth]+ | otherwise = [as]+ + extLeft = (Meta specialLeft:)+ extRight = (++[Meta specialRight])+ extBoth = extLeft . extRight - collect = ($ []) . rec- where - rec term =- case isBinary s term of- Just (a, b) -> rec a . rec b- Nothing -> (term:)+ result = concatMap (\zs -> pairingsA True zs bs) list+ make (a, b) = (construct a, construct b) + collect term =+ case getFunction term of+ Just (t, [a, b]) | s1==t -> collect a . collect b+ _ -> (term:)+ construct xs | null xs = error "associativeMatcher: empty list"- | otherwise = foldr1 (binary s) xs+ | otherwise = foldr1 (binary s1) xs+associativeMatch _ _ _ _ _ = []++specialLeft, specialRight :: Int -- special meta variables for context extension+specialLeft = maxBound+specialRight = pred specialLeft
src/Common/Strategy.hs view
@@ -16,30 +16,31 @@ ----------------------------------------------------------------------------- module Common.Strategy ( -- * Data types and type classes- Strategy, LabeledStrategy, strategyName+ Strategy, LabeledStrategy , IsStrategy(..) -- * Running strategies , fullDerivationTree, derivationTree -- * Strategy combinators -- ** Basic combinators- , (<*>), (<|>), succeed, fail, label, sequence, alternatives -- <||>+ , (<*>), (<|>), succeed, fail, label, sequence, alternatives -- ** EBNF combinators , many, many1, replicate, option -- ** Negation and greedy combinators , check, not, repeat, repeat1, try, (|>), exhaustive+ , while, until, multi -- ** Traversal combinators , fix, once, somewhere, topDown, bottomUp+ , onceWith, somewhereWith -- * Configuration combinators , module Common.Strategy.Configuration -- * Strategy locations- , StrategyLocation, topLocation, nextLocation, downLocation- , locationDepth- , subTaskLocation, nextTaskLocation, parseStrategyLocation- , StrategyOrRule, subStrategy, strategyLocations- , mapRules, mapRulesS, rulesInStrategy, cleanUpStrategy+ , strategyLocations, subStrategy+ , subTaskLocation, nextTaskLocation -- * Prefixes , Prefix, emptyPrefix, makePrefix, prefixTree, Step(..) , prefixToSteps, stepsToRules, lastStepInPrefix+ -- * Misc+ , cleanUpStrategy, rulesInStrategy, mapRules, mapRulesS ) where import Common.Strategy.Abstract@@ -47,5 +48,5 @@ import Common.Strategy.Prefix import Common.Strategy.Location import Common.Strategy.Configuration--import qualified Prelude ()+import Common.Strategy.Parsing+import Prelude ()
src/Common/Strategy/Abstract.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XFlexibleInstances #-}+{-# LANGUAGE FlexibleInstances #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -16,20 +16,21 @@ , fullDerivationTree, derivationTree, rulesInStrategy , mapRules, mapRulesS, cleanUpStrategy -- Accessors to the underlying representation- , toCore, fromCore, liftCore, liftCore2, fixCore, makeLabeledStrategy+ , toCore, fromCore, liftCore, liftCore2, makeLabeledStrategy , toLabeledStrategy- , LabelInfo, strategyName, processLabelInfo, changeInfo, makeInfo- , removed, collapsed, hidden, labelName, IsLabeled(..)+ , LabelInfo, processLabelInfo, changeInfo, makeInfo+ , removed, collapsed, hidden, IsLabeled(..) ) where +import Common.Id import Common.Utils (commaList) import Common.Strategy.Core-import Common.Strategy.BiasedChoice-import Common.Apply-import Common.Rewriting (RewriteRule(..))+import Common.Classes+import Common.Rewriting (RewriteRule) import Common.Transformation import Common.Derivation-import Common.Uniplate+import Common.Uniplate hiding (rewriteM)+import Common.Strategy.Parsing ----------------------------------------------------------- --- Strategy data-type@@ -41,13 +42,13 @@ show = show . toCore instance Apply Strategy where- applyAll s = results . fullDerivationTree s+ applyAll = runCore . toCore ----------------------------------------------------------- --- The information used as label in a strategy data LabelInfo = Info - { labelName :: String + { labelId :: Id , removed :: Bool , collapsed :: Bool , hidden :: Bool@@ -59,16 +60,20 @@ ["collapsed" | collapsed info] ++ ["hidden" | hidden info] extra = " (" ++ commaList ps ++ ")"- in show (labelName info) ++ if null ps then "" else extra+ in showId info ++ if null ps then "" else extra -makeInfo :: String -> LabelInfo-makeInfo s = Info s False False False+instance HasId LabelInfo where+ getId = labelId+ changeId f info = info { labelId = f (labelId info) }+ +makeInfo :: IsId a => a -> LabelInfo+makeInfo s = Info (newId s) False False False ----------------------------------------------------------- --- Type class -- | Type class to turn values into strategies-class Apply f => IsStrategy f where+class IsStrategy f where toStrategy :: f a -> Strategy a instance IsStrategy (Core LabelInfo) where@@ -78,20 +83,16 @@ toStrategy = id instance IsStrategy (LabeledStrategy) where- toStrategy (LS info (S core)) = - case core of- Rule Nothing r | name r == labelName info -> - S (Rule (Just info) r)- _ -> S (Label info core)+ toStrategy (LS info (S core)) = S (Label info core) -instance IsStrategy Rule where -- Major rules receive a label+instance IsStrategy Rule where toStrategy r | isMajorRule r = toStrategy (toLabeled r)- | otherwise = S (Rule Nothing r)+ | otherwise = S (Rule r) instance IsStrategy RewriteRule where toStrategy r = - toStrategy (makeRule (ruleName r) (RewriteRule r))+ toStrategy (makeRule (getId r) (makeRewriteTrans r)) ----------------------------------------------------------- --- Labeled Strategy data-type@@ -111,15 +112,16 @@ Label l c -> return (makeLabeledStrategy l (fromCore c)) _ -> fail "Strategy without label" -strategyName :: LabeledStrategy a -> String-strategyName = getLabel- instance Show (LabeledStrategy a) where show s = show (labelInfo s) ++ ": " ++ show (unlabel s) instance Apply LabeledStrategy where applyAll = applyAll . toStrategy +instance HasId (LabeledStrategy a) where+ getId = getId . labelInfo+ changeId = changeInfo . changeId+ class IsLabeled f where toLabeled :: f a -> LabeledStrategy a @@ -127,18 +129,15 @@ toLabeled = id instance IsLabeled Rule where- toLabeled r = LS (makeInfo (name r)) (S (Rule Nothing r))+ toLabeled r = LS (makeInfo (showId r)) (S (Rule r)) instance IsLabeled RewriteRule where- toLabeled r = toLabeled (makeRule (ruleName r) (RewriteRule r))+ toLabeled r = toLabeled (makeRule (showId r) (makeRewriteTrans r)) -- | Labels a strategy with a string-label :: IsStrategy f => String -> f a -> LabeledStrategy a+label :: (IsId l, IsStrategy f) => l -> f a -> LabeledStrategy a label l = LS (makeInfo l) . toStrategy -getLabel :: IsLabeled f => f a -> String-getLabel = labelName . labelInfo . toLabeled- changeInfo :: IsLabeled f => (LabelInfo -> LabelInfo) -> f a -> LabeledStrategy a changeInfo f a = LS (f info) s where LS info s = toLabeled a@@ -147,42 +146,45 @@ --- Process Label Information processLabelInfo :: (l -> LabelInfo) -> Core l a -> Core l a-processLabelInfo getInfo = mapCore forLabel forRule+processLabelInfo getInfo = rec emptyCoreEnv where- forLabel l c + rec env core = + case core of + Rec n a -> Rec n (rec (insertCoreEnv n core env) a)+ Label l a -> forLabel env l (rec env a)+ _ -> descend (rec env) core+ + forLabel env l c | removed info = Fail- | collapsed info = Rule (Just l) asRule+ | collapsed info = Label l (Rule asRule) -- !! | otherwise = new where new | hidden info = mapRule minorRule (Label l c) | otherwise = Label l c info = getInfo l- asRule = makeSimpleRuleList (labelName info ++ " (collapsed)") (applyAll new)- forRule (Just l) r - | removed info = Fail- | hidden info = Rule (Just l) (minorRule r)- | otherwise = Rule (Just l) r- where- info = getInfo l- forRule _ r = Rule Nothing r+ asRule = makeSimpleRuleList (showId info{- ++ " (collapsed)"-}) + (runCoreWith env new) ----------------------------------------------------------- --- Remaining functions -- | Returns the derivation tree for a strategy and a term, including all -- minor rules-fullDerivationTree :: IsStrategy f => f a -> a -> DerivationTree (Rule a) a-fullDerivationTree = makeBiasedTree p . processLabelInfo id . toCore . toStrategy +fullDerivationTree :: IsStrategy f => f a -> a -> DerivationTree (Step LabelInfo a) a+fullDerivationTree = make . processLabelInfo id . toCore . toStrategy where - p t = endpoint t || any isMajorRule (annotations t) || any p (subtrees t)-+ make core = fmap value . parseDerivationTree . makeState core+ -- | Returns the derivation tree for a strategy and a term with only major rules derivationTree :: IsStrategy f => f a -> a -> DerivationTree (Rule a) a-derivationTree s = mergeSteps isMajorRule . fullDerivationTree s-+derivationTree s = mergeMaybeSteps . mapSteps f . fullDerivationTree s+ where+ f (RuleStep r) | isMajorRule r = Just r+ f _ = Nothing+ -- | Returns a list of all major rules that are part of a labeled strategy rulesInStrategy :: IsStrategy f => f a -> [Rule a]-rulesInStrategy f = [ r | Rule _ r <- universe (toCore (toStrategy f)), isMajorRule r ]+rulesInStrategy f = [ r | Rule r <- universe (toCore (toStrategy f)), isMajorRule r ] -- | Apply a function to all the rules that make up a labeled strategy mapRules :: (Rule a -> Rule b) -> LabeledStrategy a -> LabeledStrategy b@@ -195,7 +197,7 @@ cleanUpStrategy :: (a -> a) -> LabeledStrategy a -> LabeledStrategy a cleanUpStrategy f (LS n s) = mapRules g (LS n (S core)) where- core = Rule Nothing (doAfter f idRule) :*: toCore s+ core = Rule (doAfter f idRule) :*: toCore s g r | isMajorRule r = doAfter f r | otherwise = r @@ -210,9 +212,3 @@ liftCore2 :: (IsStrategy f, IsStrategy g) => (Core LabelInfo a -> Core LabelInfo a -> Core LabelInfo a) -> f a -> g a -> Strategy a liftCore2 f = liftCore . f . toCore . toStrategy--fixCore :: (Core l a -> Core l a) -> Core l a-fixCore f = Rec i (f (Var i)) -- disadvantage: function f is applied twice- where- s = coreVars (f (Rule Nothing idRule))- i = if null s then 0 else maximum s + 1
− src/Common/Strategy/BiasedChoice.hs
@@ -1,106 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Common.Strategy.BiasedChoice - ( Bias(..), placeBiasLabels, biasTreeG, makeBiasedTree- ) where--import Common.Apply--- import Common.View-import Common.Derivation-import Common.Transformation-import Common.Strategy.Core--- import Common.Uniplate--data Bias f a = TryFirst BiasId | OrElse BiasId | Normal (f a) deriving Show-type BiasId = Int--instance Apply f => Apply (Bias f) where- applyAll (Normal r) = applyAll r- applyAll _ = return---- Disabled! -placeBiasLabels :: Core l a -> Core (Either (Bias f a) l) a-placeBiasLabels = {-fst . rec 0 . -}mapLabel Right- where {-- -- Left-biased choice- rec n (a :|>: b) = - let (ra, n1) = rec n a- (rb, n2) = rec n1 b- left = Label (Left (TryFirst n)) ra- right = Label (Left (OrElse n)) rb- in (left :|: right, n2)- -- All other cases- rec n core = - let (cs, f) = uniplate core- in first f (recList n cs)- - recList n [] = ([], n)- recList n (x:xs) = - let (a, n1) = rec n x- (as, n2) = recList n1 xs- in (a:as, n2) -}--biasTranslation :: (Rule a -> f a) -> Translation (Either (Bias f a) l) a (Bias f a)-biasTranslation f = (either Before (const Skip), Normal . f)--biasTreeG :: (DerivationTree (f a, info) a -> Bool) -> DerivationTree (Bias f a, info) a -> DerivationTree (f a, info) a-biasTreeG success t = t {branches = f [] (branches t)}- where- f _ [] = []- f env (((bias, info), st):xs) = - case bias of- TryFirst n- | success new -> branches new ++ f (n:env) xs- | otherwise -> f env xs- where new = biasTreeG success st- OrElse n - | n `elem` env -> f env xs- | otherwise -> branches (biasTreeG success st) ++ f env xs- Normal r -> ((r, info), biasTreeG success st):f env xs---- success :: DerivationTree s a -> Bool--- success = isJust . derivation--biasTree :: (DerivationTree (f a) a -> Bool) -> DerivationTree (Bias f a) a -> DerivationTree (f a) a-biasTree success t = t {branches = f [] (branches t)}- where- f _ [] = []- f env ((bias, st):xs) = - case bias of- TryFirst n- | success new -> branches new ++ f (n:env) xs- | otherwise -> f env xs- where new = biasTree success st- OrElse n - | n `elem` env -> f env xs- | otherwise -> branches (biasTree success st) ++ f env xs- Normal r -> (r, biasTree success st):f env xs-{-- success :: DerivationTree s a -> Bool- success = isJust . derivation -}- -makeBiasedTree :: (DerivationTree (Rule a) a -> Bool) -> Core l a -> a -> DerivationTree (Rule a) a-makeBiasedTree p core = - biasTree p . changeLabel fst . runTree (strategyTree (biasTranslation id) (placeBiasLabels core))- ---------------------------{--test = makeBiasedTree (maybe False (const True) . derivation) myCore 5--myCore = (r1 :|>: r2) :|: (r3 :|>: r4)- where- r1 = make "r1" $ \n -> trace "**1**" [n*n]- r2 = make "r2" $ \n -> trace "**2**" [n+1]- r3 = make "r3" $ \n -> trace "**3**" [n*2]- r4 = make "r4" $ \n -> trace "**4**" [n `div` 2]- trace _ = id- make n = Rule Nothing . minorRule . makeSimpleRuleList n -}
src/Common/Strategy/Combinators.hs view
@@ -14,12 +14,16 @@ ----------------------------------------------------------------------------- module Common.Strategy.Combinators where +import qualified Prelude import Prelude hiding (not, repeat, fail, sequence)+import Common.Id import Common.Context import Common.Navigator import Common.Transformation import Common.Strategy.Core import Common.Strategy.Abstract+import Common.Strategy.Configuration+import Data.Maybe ----------------------------------------------------------- --- Strategy combinators@@ -32,11 +36,21 @@ -- | Put two strategies in sequence (first do this, then do that) (<*>) :: (IsStrategy f, IsStrategy g) => f a -> g a -> Strategy a-(<*>) = liftCore2 (:*:)+(<*>) = liftCore2 $ \x y -> + case (x, y) of+ (Succeed, _) -> y+ (_, Succeed) -> x+ (Fail, _) -> Fail+ (_, Fail) -> Fail+ _ -> x :*: y -- | Choose between the two strategies (either do this or do that) (<|>) :: (IsStrategy f, IsStrategy g) => f a -> g a -> Strategy a-(<|>) = liftCore2 (:|:)+(<|>) = liftCore2 $ \x y ->+ case (x, y) of+ (Fail, _) -> y+ (_, Fail) -> x+ _ -> x :|: y -- | The strategy that always succeeds (without doing anything) succeed :: Strategy a@@ -101,6 +115,18 @@ (|>) :: (IsStrategy f, IsStrategy g) => f a -> g a -> Strategy a (|>) = liftCore2 (:|>:) +-- | Repeat the strategy as long as the predicate holds+while :: IsStrategy f => (a -> Bool) -> f a -> Strategy a+while p s = repeat (check p <*> s)++-- | Repeat the strategy until the predicate holds+until :: IsStrategy f => (a -> Bool) -> f a -> Strategy a+until p = while (Prelude.not . p)++-- | Apply a strategy at least once, but collapse into a single step+multi :: (IsId l, IsStrategy f) => l -> f a -> LabeledStrategy a+multi s = collapse . label s . repeat1+ -- | Apply the strategies from the list exhaustively (until this is no longer possible) exhaustive :: IsStrategy f => [f a] -> Strategy a exhaustive = repeat . alternatives@@ -110,19 +136,35 @@ -- | A fix-point combinator on strategies (to model recursion). Powerful -- (but dangerous) combinator fix :: (Strategy a -> Strategy a) -> Strategy a-fix f = fromCore (fixCore (toCore . f . fromCore))+fix f = fromCore (coreFix (toCore . f . fromCore)) +-- | Apply a strategy on (exactly) one of the term's direct children. The+-- function selects which children are visited.+onceWith :: IsStrategy f => String -> (Context a -> [Int]) -> f (Context a) -> Strategy (Context a)+onceWith n f s = ruleMoveDown <*> s <*> ruleMoveUp+ where+ ruleMoveDown = minorRule $ makeSimpleRuleList ("navigation.down." ++ n) $ \a -> + concatMap (`down` a) (f a)+ ruleMoveUp = minorRule $ makeSimpleRule "navigation.up" $ \a ->+ Just (fromMaybe a (up a))++-- | Apply a strategy somewhere in the term. The function selects which +-- children are visited+somewhereWith :: IsStrategy f => String -> (Context a -> [Int]) -> f (Context a) -> Strategy (Context a)+somewhereWith n f s = fix $ \this -> s <|> onceWith n f this+ -- | Apply a strategy on (exactly) one of the term's direct children once :: IsStrategy f => f (Context a) -> Strategy (Context a)-once s = ruleMoveDown <*> s <*> ruleMoveUp- where- ruleMoveDown = minorRule $ makeSimpleRuleList "MoveDown" allDowns - ruleMoveUp = minorRule $ makeSimpleRule "MoveUp" up+once = onceWith "all" visitAll -- | Apply a strategy somewhere in the term somewhere :: IsStrategy f => f (Context a) -> Strategy (Context a)-somewhere s = fix $ \this -> s <|> once this+somewhere = somewhereWith "all" visitAll +-- local helper+visitAll :: Context a -> [Int]+visitAll a = [ 0 .. arity a-1 ]+ -- | Search for a suitable location in the term to apply the strategy using a -- top-down approach topDown :: IsStrategy f => f (Context a) -> Strategy (Context a)@@ -131,8 +173,4 @@ -- | Search for a suitable location in the term to apply the strategy using a -- bottom-up approach bottomUp :: IsStrategy f => f (Context a) -> Strategy (Context a)-bottomUp s = fix $ \this -> once this <|> (not (once (bottomUp s)) <*> s)--{- The ideal implementation does not yet work: there appears to be a strange- interplay between the fixpoint operator (with variables) and the not combinator- > bottomUp s = fix $ \this -> once this |> s -}+bottomUp s = fix $ \this -> once this |> s
src/Common/Strategy/Configuration.hs view
@@ -12,17 +12,18 @@ module Common.Strategy.Configuration ( -- Types and constructors StrategyConfiguration, ConfigItem- , ConfigLocation(..), ConfigAction(..), configActions+ , ConfigLocation, byName, byGroup+ , ConfigAction(..), configActions -- Configure- , configure+ , configure, configureNow -- Combinators , remove, reinsert, collapse, expand, hide, reveal ) where +import Common.Id import Common.Strategy.Abstract import Common.Strategy.Core-import Common.Strategy.Location-import Common.Transformation+import Data.Maybe --------------------------------------------------------------------- -- Types and constructors@@ -31,9 +32,8 @@ type ConfigItem = (ConfigLocation, ConfigAction) data ConfigLocation- = ByName String- | ByGroup String- | ByLocation StrategyLocation+ = ByName Id+ | ByGroup Id deriving Show data ConfigAction = Remove | Reinsert | Collapse | Expand | Hide | Reveal@@ -42,31 +42,39 @@ configActions :: [ConfigAction] configActions = [Remove .. ] +byName :: HasId a => a -> ConfigLocation+byName = ByName . getId++byGroup :: HasId a => a -> ConfigLocation+byGroup = ByGroup . getId+ --------------------------------------------------------------------- -- Configure +configureNow :: LabeledStrategy a -> LabeledStrategy a+configureNow = + let lsToCore = toCore . toStrategy+ coreToLS = fromMaybe err . toLabeledStrategy . toStrategy+ err = error "configureNow: label disappeared"+ in coreToLS . processLabelInfo id . lsToCore+ configure :: StrategyConfiguration -> LabeledStrategy a -> LabeledStrategy a configure cfg ls = - label (strategyName ls) (configureCore cfg (toCore (unlabel ls)))+ label (showId ls) (configureCore cfg (toCore (unlabel ls))) configureCore :: StrategyConfiguration -> Core LabelInfo a -> Core LabelInfo a-configureCore cfg = mapCore f g . addLocation+configureCore cfg = mapLabel (change []) where- f pair a = Label (change pair []) a- g (Just pair) r = Rule (Just (change pair (ruleGroups r))) r- g Nothing r = Rule Nothing r- - change pair@(_, info) groups = - let actions = getActions pair groups cfg+ change groups info = + let actions = getActions info groups cfg in foldr doAction info actions -getActions :: (StrategyLocation, LabelInfo) -> [String] +getActions :: LabelInfo -> [String] -> StrategyConfiguration -> [ConfigAction]-getActions (loc, info) groups = map snd . filter (select . fst)+getActions info groups = map snd . filter (select . fst) where- select (ByName s) = labelName info == s- select (ByGroup s) = s `elem` groups- select (ByLocation l) = loc == l+ select (ByName a) = getId info == a+ select (ByGroup s) = showId s `elem` groups doAction :: ConfigAction -> LabelInfo -> LabelInfo doAction action =
src/Common/Strategy/Core.hs view
@@ -15,18 +15,15 @@ ----------------------------------------------------------------------------- module Common.Strategy.Core ( Core(..)- , strategyTree, runTree --, makeTree - , mapRule, coreVars, noLabels, mapCore, mapCoreM --, catMaybeLabel --, , - , mapLabel, Translation, ForLabel(..) --, simpleTranslation+ , mapRule, mapLabel, noLabels+ , coreMany, coreRepeat, coreOrElse, coreFix+ , CoreEnv, emptyCoreEnv, insertCoreEnv, lookupCoreEnv, substCoreEnv ) where -import qualified Common.Strategy.Grammar as Grammar-import Common.Strategy.Grammar (Grammar, (<*>), (<|>), symbol)-import Common.Apply-import Common.Derivation import Common.Transformation import Common.Uniplate-import Control.Monad.Identity+import Data.Maybe+import qualified Data.IntMap as IM ----------------------------------------------------------------- -- Strategy (internal) data structure, containing a selection@@ -42,11 +39,11 @@ | Core l a :|>: Core l a | Many (Core l a) | Repeat (Core l a)- | Not (Core () a) -- proves that there are no labels inside+ | Not (Core l a) | Label l (Core l a) | Succeed | Fail- | Rule (Maybe l) (Rule a)+ | Rule (Rule a) | Var Int | Rec Int (Core l a) deriving Show@@ -54,9 +51,6 @@ ----------------------------------------------------------------- -- Useful instances -instance Apply (Core l) where - applyAll core = results . makeTree core- instance Uniplate (Core l a) where uniplate core = case core of@@ -67,118 +61,91 @@ Repeat a -> ([a], \[x] -> Repeat x) Label l a -> ([a], \[x] -> Label l x) Rec n a -> ([a], \[x] -> Rec n x)- Not a -> ([noLabels a], \[x] -> Not (noLabels x))+ Not a -> ([a], \[x] -> Not x) _ -> ([], \_ -> core) -------------------------------------------------------------------- The strategy tree (static, no term)+-- Core environment -strategyTree :: Translation l a b -> Core l a -> DerivationTree b ()-strategyTree t = grammarTree . toGrammar t+newtype CoreEnv l a = CE (IM.IntMap (Core l a)) -grammarTree :: Grammar a -> DerivationTree a ()-grammarTree gr = addBranches list node- where - node = singleNode () (Grammar.empty gr)- list = [ (f, grammarTree rest) | (f, rest) <- Grammar.firsts gr ]+emptyCoreEnv :: CoreEnv l a+emptyCoreEnv = CE IM.empty+ +insertCoreEnv :: Int -> Core l a -> CoreEnv l a -> CoreEnv l a+insertCoreEnv n a (CE m) = CE (IM.insert n a m) --------------------------------------------------------------------- Running a strategy+deleteCoreEnv :: Int -> CoreEnv l a -> CoreEnv l a+deleteCoreEnv n (CE m) = CE (IM.delete n m) -makeTree :: Core l a -> a -> DerivationTree (Rule a) a-makeTree c = changeLabel fst . runTree (strategyTree simpleTranslation c)+lookupCoreEnv :: Int -> CoreEnv l a -> Maybe (Core l a)+lookupCoreEnv n (CE m) = IM.lookup n m -runTree :: Apply f => DerivationTree (f a) info -> a -> DerivationTree (f a, info) a-runTree t a = addBranches list (singleNode a (endpoint t))- where- list = concatMap make (branches t)- make (f, st) = [ ((f, root st), runTree st b) | b <- applyAll f a ]+substCoreEnv :: CoreEnv l a -> Core l a -> Core l a+substCoreEnv env core = + case core of+ Var i -> fromMaybe core (lookupCoreEnv i env)+ Rec i a -> Rec i (substCoreEnv (deleteCoreEnv i env) a)+ _ -> descend (substCoreEnv env) core -------------------------------------------------------------------- Translation to Grammar data type+-- Definitions -type Translation l a b = (l -> ForLabel b, Rule a -> b)+coreMany :: Core l a -> Core l a+coreMany a = Rec n (Succeed :|: (a :*: Var n))+ where n = nextVar a -data ForLabel a = Skip | Before a | After a | Around a a+coreRepeat :: Core l a -> Core l a+coreRepeat a = Many a :*: Not a -simpleTranslation :: Translation l a (Rule a)-simpleTranslation = (const Skip, id)+coreOrElse :: Core l a -> Core l a -> Core l a+coreOrElse a b = a :|: (Not a :*: b) -toGrammar :: Translation l a b -> Core l a -> Grammar b-toGrammar (f, g) = rec- where- rec core =- case core of- a :*: b -> rec a <*> rec b- a :|: b -> rec a <|> rec b- a :|>: b -> rec (a :|: (Not (noLabels a) :*: b))- Many a -> Grammar.many (rec a)- Repeat a -> rec (Many a :*: Not (noLabels a))- Succeed -> Grammar.succeed- Fail -> Grammar.fail- Label l a -> forLabel l (rec a)- Rule ml r -> (maybe id forLabel ml) (symbol (g r))- Var n -> Grammar.var n- Rec n a -> Grammar.rec n (rec a)- Not a -> symbol (g (notRule a))- - forLabel l g =- case f l of- Skip -> g- Before s -> symbol s <*> g- After t -> g <*> symbol t- Around s t -> symbol s <*> g <*> symbol t+coreFix :: (Core l a -> Core l a) -> Core l a+coreFix f = -- disadvantage: function f is applied twice+ let i = nextVar (f (Var (-1)))+ in Rec i (f (Var i)) -notRule :: Apply f => f a -> Rule a-notRule f = checkRule (not . applicable f)- +nextVar :: Core l a -> Int+nextVar p+ | null xs = 0+ | otherwise = maximum xs + 1+ where xs = coreVars p++coreVars :: Core l a -> [Int]+coreVars core = + case core of+ Var n -> [n]+ Rec n a -> n : coreVars a+ _ -> concatMap coreVars (children core)+ ----------------------------------------------------------------- -- Utility functions mapLabel :: (l -> m) -> Core l a -> Core m a-mapLabel f = mapCore (Label . f) (Rule . fmap f)+mapLabel f = mapCore (Label . f) Rule mapRule :: (Rule a -> Rule b) -> Core l a -> Core l b-mapRule f = mapCore Label (\ml -> Rule ml . f)+mapRule f = mapCore Label (Rule . f) noLabels :: Core l a -> Core m a-noLabels = mapCore (const id) (const (Rule Nothing))- --- catMaybeLabel :: Core (Maybe l) a -> Core l a--- catMaybeLabel = mapCore (maybe id Label) (Rule . join)+noLabels = mapCore (const id) Rule -mapCore :: (l -> Core m b -> Core m b) -> (Maybe l -> Rule a -> Core m b) +mapCore :: (l -> Core m b -> Core m b) -> (Rule a -> Core m b) -> Core l a -> Core m b-mapCore f g = - let fm l = return . f l . runIdentity- gm l = return . g l- in runIdentity . mapCoreM fm gm---- The most primitive function that applies functions to the label and --- rule alternatives. Monadic version.-mapCoreM :: Monad m => (k -> m (Core l b) -> m (Core l b)) - -> (Maybe k -> Rule a -> m (Core l b)) - -> Core k a -> m (Core l b)-mapCoreM f g = rec - where +mapCore f g = rec+ where rec core = case core of- a :*: b -> liftM2 (:*:) (rec a) (rec b)- a :|: b -> liftM2 (:|:) (rec a) (rec b)- a :|>: b -> liftM2 (:|>:) (rec a) (rec b)- Many a -> liftM Many (rec a)- Repeat a -> liftM Repeat (rec a)- Succeed -> return Succeed- Fail -> return Fail+ a :*: b -> rec a :*: rec b+ a :|: b -> rec a :|: rec b+ a :|>: b -> rec a :|>: rec b+ Many a -> Many (rec a)+ Repeat a -> Repeat (rec a)+ Succeed -> Succeed+ Fail -> Fail Label l a -> f l (rec a)- Rule ml r -> g ml r- Var n -> return (Var n)- Rec n a -> liftM (Rec n) (rec a)- Not a -> do - let recNot h = mapCoreM (const id) (const h)- b <- recNot (g Nothing) a- c <- recNot (return . Rule Nothing) b- return (Not c)- -coreVars :: Core l a -> [Int]-coreVars s = [ n | Rec n _ <- universe s ] ++ [ n | Var n <- universe s ]+ Rule r -> g r+ Var n -> Var n+ Rec n a -> Rec n (rec a)+ Not a -> Not (rec a)
− src/Common/Strategy/Grammar.hs
@@ -1,367 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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)------ This module defines a set of combinators for context-free grammars. These--- grammars are the basis of the strategies. The fix-point combinator 'fix' --- makes it context-free. The code is based on the RTS'08 paper--- "Recognizing Strategies"----------------------------------------------------------------------------------module Common.Strategy.Grammar- ( -- * Abstract data type- Grammar- -- * Smart constructor functions- , (<*>), (<|>), (<||>), var, rec, fix, many, succeed, fail, symbol- -- * Elementary operations- , empty, firsts, nonempty - -- * Membership and generated language- , member, language, languageBF- -- * Additional functions- , collectSymbols, join, withIndex- -- * QuickCheck properties- , checks- ) where--import Common.Uniplate-import Control.Monad (liftM, liftM2)-import Data.List-import Prelude hiding (fail)-import Test.QuickCheck-import qualified Data.Set as S--------------------------------------------------------------------------- Abstract data type--data Grammar a = Grammar a :*: Grammar a - | Grammar a :|: Grammar a - | Grammar a :||: Grammar a- | Rec Int (Grammar a) - | Symbol a | Var Int | Succeed | Fail deriving Show--infixr 3 :|:, <|>-infixr 4 :||:, <||>-infixr 5 :*:, <*>--------------------------------------------------------------------------- Smart constructor functions---- simple constructors-succeed, fail :: Grammar a-var :: Int -> Grammar a-symbol :: a -> Grammar a--succeed = Succeed-fail = Fail -symbol = Symbol-var = Var---- | Smart constructor for sequences: removes fails and succeeds in the--- operands-(<*>) :: Grammar a -> Grammar a -> Grammar a-Succeed <*> t = t-s <*> Succeed = s-Fail <*> _ = fail-_ <*> Fail = fail-(s :*: t) <*> u = s :*: (t <*> u)-s <*> t = s :*: t---- | Smart constructor for alternatives: removes fails in the operands, and --- merges succeeds if present in both arguments-(<|>) :: Grammar a -> Grammar a -> Grammar a-Fail <|> t = t-s <|> Fail = s-(s :|: t) <|> u = s :|: (t <|> u)-Succeed <|> Succeed = Succeed-s <|> t = s :|: t---- | Smart constructor for parallel execution: removes fails and succeeds in the operands-(<||>) :: Grammar a -> Grammar a -> Grammar a-Succeed <||> t = t-s <||> Succeed = s-Fail <||> _ = fail-_ <||> Fail = fail-(s :||: t) <||> u = s :||: (t <||> u)-s <||> t = s :||: t---- | For constructing a recursive grammar-rec :: Int -> Grammar a -> Grammar a-rec i s = if i `S.member` freeVars s then Rec i s else s----- | Fix-point combinator to model recursion. Be careful: this combinator is --- VERY powerfull, and it is your own responsibility that the result--- is a valid, non-left-recursive grammar-fix :: (Grammar a -> Grammar a) -> Grammar a-fix f = Rec i (f (Var i)) -- disadvantage: function f is applied twice- where- s = allVars (f Succeed)- i = if S.null s then 0 else S.findMax s + 1---- | Zero or more occurrences-many :: Grammar a -> Grammar a-many s = rec 0 (succeed <|> (nonempty s <*> var 0))-{- TODO: deal with free variables?-many s = rec i (succeed <|> (nonempty s <*> var i))- where- vs = freeVars s- i = if S.null vs then 0 else 1 + S.findMax vs -}- -------------------------------------------------------------------------- Elementary operations---- | Tests whether the grammar accepts the empty string-empty :: Grammar a -> Bool-empty (s :*: t) = empty s && empty t-empty (s :|: t) = empty s || empty t-empty (s :||: t) = empty s && empty t-empty (Rec _ s) = empty s-empty Succeed = True-empty _ = False---- | Returns the firsts set of the grammar, where each symbol is--- paired with the remaining grammar-firsts :: Grammar a -> [(a, Grammar a)]-firsts (s :*: t) = [ (a, s' <*> t) | (a, s') <- firsts s ] ++- (if empty s then firsts t else [])-firsts (s :|: t) = firsts s ++ firsts t-firsts (s :||: t) = [ (a, s' <||> t ) | (a, s') <- firsts s ] ++- [ (a, s <||> t' ) | (a, t') <- firsts t]-firsts (Rec i s) = firsts (replaceVar i (Rec i s) s)-firsts (Symbol a) = [(a, succeed)]-firsts _ = []---- | Returns the grammar without the empty string alternative-nonempty :: Grammar a -> Grammar a-nonempty s = foldr (<|>) fail [ symbol a <*> t | (a, t) <- firsts s ]--------------------------------------------------------------------------- Membership and generated language---- | Checks whether a string is member of the grammar's language-member :: Eq a => [a] -> Grammar a -> Bool-member [] g = empty g-member (a:as) g = not $ null [ () | (b, t) <- firsts g, a==b, member as t ]---- | Generates the language of the grammar (list can be infinite). The sentences are --- returned sorted by length, thus in a breadth-first order. The integer that is passed--- is the cut-off depth (the maximal length of the sentences) needed to avoid non-termination-language :: Int -> Grammar a -> [[a]]-language n = concat . take n . languageBF---- | Generates the language of a grammar in a breadth-first manner, which is made explicit--- by the outermost list. Sentences are grouped by their length-languageBF :: Grammar a -> [[[a]]]-languageBF s = [ [] | empty s ] : merge [ map (map (a:)) $ languageBF t | (a, t) <- firsts s ]- where merge = map concat . transpose--------------------------------------------------------------------------- Additional functions---- | Collect all the symbols of the grammar-collectSymbols :: Grammar a -> [a]-collectSymbols (Symbol a) = [a]-collectSymbols g = compos [] (++) collectSymbols g---- | The (monadic) join -join :: Grammar (Grammar a) -> Grammar a-join = mapSymbol id---- | Label all symbols with an index (from left to right)-withIndex :: Grammar a -> Grammar (Int, a)-withIndex = snd . rec 0- where- rec :: Int -> Grammar a -> (Int, Grammar (Int, a))- rec n grammar =- case grammar of - p :*: q -> let (n1, a) = rec n p- (n2, b) = rec n1 q- in (n2, a :*: b)- p :|: q -> let (n1, a) = rec n p- (n2, b) = rec n1 q- in (n2, a :|: b)- p :||: q -> let (n1, a) = rec n p- (n2, b) = rec n1 q- in (n2, a :||: b)- Rec i s -> let (n1, a) = rec n s- in (n1, Rec i a)- Var i -> (n, Var i)- Symbol a -> (n+1, Symbol (n, a))- Succeed -> (n, Succeed)- Fail -> (n, Fail)--------------------------------------------------------------------------- Local helper functions and instances--instance Uniplate (Grammar a) where- uniplate (s :*: t) = ([s,t], \[a,b] -> a :*: b)- uniplate (s :|: t) = ([s,t], \[a,b] -> a :|: b)- uniplate (s :||: t) = ([s,t], \[a,b] -> a :||: b)- uniplate (Rec i s) = ([s] , \[a] -> Rec i a)- uniplate g = ([] , \[] -> g)--instance Functor Grammar where- fmap f = mapSymbol (symbol . f)--freeVars :: Grammar a -> S.Set Int-freeVars (Rec i s) = freeVars s S.\\ S.singleton i-freeVars (Var i) = S.singleton i-freeVars g = compos S.empty S.union freeVars g--allVars :: Grammar a -> S.Set Int-allVars (Var i) = S.singleton i-allVars g = compos S.empty S.union allVars g--replaceVar :: Int -> Grammar a -> Grammar a -> Grammar a-replaceVar i new = rec - where- rec g =- case g of - Var j | i==j -> new- Rec j _ | i==j -> g- _ -> f $ map rec cs- where (cs, f) = uniplate g--mapSymbol :: (a -> Grammar b) -> Grammar a -> Grammar b-mapSymbol f (p :*: q) = mapSymbol f p <*> mapSymbol f q-mapSymbol f (p :|: q) = mapSymbol f p <|> mapSymbol f q-mapSymbol f (p :||: q) = mapSymbol f p <||> mapSymbol f q-mapSymbol f (Rec i p) = Rec i (mapSymbol f p) -mapSymbol _ (Var i) = Var i-mapSymbol f (Symbol a) = f a-mapSymbol _ Succeed = Succeed-mapSymbol _ Fail = Fail------------------------------------------------------------- QuickCheck generator--instance Arbitrary a => Arbitrary (Grammar a) where- arbitrary = sized (arbGrammar [])-instance CoArbitrary a => CoArbitrary (Grammar a) where- coarbitrary grammar =- case grammar of- p :*: q -> variant 0 . coarbitrary p . coarbitrary q- p :|: q -> variant 1 . coarbitrary p . coarbitrary q- p :||: q -> variant 2 . coarbitrary p . coarbitrary q- Rec i p -> variant 3 . coarbitrary i . coarbitrary p- Var i -> variant 4 . coarbitrary i- Symbol a -> variant 5 . coarbitrary a- Succeed -> variant 6- Fail -> variant 7---- Use smart constructors here-arbGrammar :: Arbitrary a => [Grammar a] -> Int -> Gen (Grammar a)-arbGrammar xs n- | n == 0 = oneof $- liftM symbol arbitrary :- map return ([succeed, fail] ++ xs)- | otherwise = oneof- [ arbGrammar xs 0- , liftM2 (<*>) rec rec- , liftM2 (<|>) rec rec- , liftM2 (<||>) rec rec- , liftM many rec--- , liftM fix (promote (\x -> arbGrammar (x:xs) (n `div` 2)))-{- , do i <- oneof $ map return [1::Int ..5]- x <- arbGrammar (Var i:xs) (n `div` 2)- return $ Rec i x -}- ]- where - rec = arbGrammar xs (n `div` 2)- ------------------------------------------------------------ QuickCheck properties --propSymbols :: (Int -> Int) -> Grammar Int -> Bool-propSymbols f p = map f (collectSymbols p) == collectSymbols (fmap f p)--propIndexId :: Grammar Int -> Bool-propIndexId p = fmap snd (withIndex p) === p--propIndexUnique :: Grammar Int -> Bool-propIndexUnique p = is == nub is- where is = map fst $ collectSymbols $ withIndex p--propSound :: Grammar Int -> Property-propSound p = not (null xs) ==> all (`member` p) xs- where xs = take 20 $ language 10 p--propEmpty :: Grammar Int -> Bool-propEmpty s = empty s == member [] s--propNonEmpty :: Grammar Int -> Bool-propNonEmpty = not . member [] . nonempty--propSplitSucceed :: Grammar Int -> Bool-propSplitSucceed p = p === if empty p then succeed <|> new else new- where new = nonempty p--propFirsts :: Grammar Int -> Bool-propFirsts p = nonempty p === foldr op fail (firsts p)- where op (a, q) r = (symbol a <*> q) <|> r--propJoin :: Grammar Int -> Bool-propJoin p = join (fmap symbol p) === p- -propMap :: (Int -> Int) -> (Int -> Int) -> Grammar Int -> Bool-propMap f g p = fmap (f . g) p === fmap (f . g) p--propRec :: Grammar Int -> Property-propRec this@(Rec i p) = property (replaceVar i this p === this)-propRec _ = False ==> True--propSucceed :: Grammar Int -> Bool-propSucceed p = empty p == member [] p--infixl 1 ===- -(===) :: Grammar Int -> Grammar Int -> Bool-p === q = all (`member` p) ys && all (`member` q) xs - where- xs = take 20 $ language 10 p- ys = take 20 $ language 10 q- -associative op p q r = p `op` (q `op` r) === (p `op` q) `op` r-commutative op p q = p `op` q === q `op` p-idempotent op p = p `op` p === p-leftUnit op e p = e `op` p === p-rightUnit op e p = p `op` e === p-unit op e p = leftUnit op e p && rightUnit op e p-absorbe op e p = (e `op` p === e) && (p `op` e === e)-propStar p = many p === succeed <|> (p <*> many p)-propStarStar p = many (many p) === many p--checks :: IO ()-checks = do- putStrLn "** Grammar combinators"- quickCheck propMap- quickCheck propJoin- quickCheck propSymbols- quickCheck propIndexId- quickCheck propIndexUnique- quickCheck propSound- quickCheck propEmpty- quickCheck propNonEmpty- quickCheck propSplitSucceed- quickCheck propFirsts- quickCheck propRec- quickCheck propStar- quickCheck propStarStar- quickCheck propSucceed- quickCheck $ associative (<|>)- quickCheck $ commutative (<|>)- quickCheck $ idempotent (<|>)- quickCheck $ unit (<|>) fail- quickCheck $ associative (<*>)- quickCheck $ unit (<*>) succeed- quickCheck $ absorbe (<*>) fail- quickCheck $ associative (<||>)- quickCheck $ commutative (<||>)- quickCheck $ unit (<||>) succeed- quickCheck $ absorbe (<||>) fail
src/Common/Strategy/Location.hs view
@@ -12,109 +12,69 @@ -- ----------------------------------------------------------------------------- module Common.Strategy.Location - ( StrategyLocation, topLocation, nextLocation, downLocation- , locationDepth- , subTaskLocation, nextTaskLocation, parseStrategyLocation- , StrategyOrRule, strategyLocations, subStrategy, addLocation+ ( subTaskLocation, nextTaskLocation+ , strategyLocations, subStrategy ) where +import Common.Id import Common.Strategy.Abstract import Common.Strategy.Core-import Common.Transformation import Common.Uniplate-import Common.Utils (readM)-import Data.Foldable (toList)-import Data.Sequence hiding (take)-import Control.Monad.State+import Common.Utils (safeHead)+import Data.Maybe ----------------------------------------------------------- --- Strategy locations --- | A strategy location corresponds to a substrategy or a rule-newtype StrategyLocation = SL (Seq Int)- deriving Eq--instance Show StrategyLocation where- show (SL xs) = show (toList xs)--type StrategyOrRule a = Either (LabeledStrategy a) (Rule a)--topLocation :: StrategyLocation -topLocation = SL empty--nextLocation :: StrategyLocation -> StrategyLocation-nextLocation (SL xs) =- case viewr xs of- EmptyR -> topLocation -- invalid- ys :> a -> SL (ys |> (a+1))--downLocation :: StrategyLocation -> StrategyLocation-downLocation (SL xs) = SL (xs |> 0)--locationDepth :: StrategyLocation -> Int-locationDepth (SL xs) = Data.Sequence.length xs- -- old (current) and actual (next major rule) location-subTaskLocation :: StrategyLocation -> StrategyLocation -> StrategyLocation-subTaskLocation (SL xs) (SL ys) = SL (rec xs ys)+subTaskLocation :: LabeledStrategy a -> Id -> Id -> Id+subTaskLocation s xs ys = g (rec (f xs) (f ys)) where- rec xs ys =- case (viewl xs, viewl ys) of- (i :< is, j :< js) - | i == j -> i <| rec is js - | otherwise -> empty- (_, j :< _) -> singleton j- _ -> empty+ f = fromMaybe [] . toLoc s+ g = fromMaybe (getId s) . fromLoc s+ rec (i:is) (j:js)+ | i == j = i : rec is js + | otherwise = []+ rec _ (j:_) = [j]+ rec _ _ = [] -- old (current) and actual (next major rule) location-nextTaskLocation :: StrategyLocation -> StrategyLocation -> StrategyLocation-nextTaskLocation (SL xs) (SL ys) = SL (rec xs ys)+nextTaskLocation :: LabeledStrategy a -> Id -> Id -> Id+nextTaskLocation s xs ys = g (rec (f xs) (f ys)) where- rec xs ys =- case (viewl xs, viewl ys) of- (i :< is, j :< js)- | i == j -> i <| rec is js- | otherwise -> singleton j- _ -> empty--parseStrategyLocation :: String -> Maybe StrategyLocation-parseStrategyLocation = fmap (SL . fromList) . readM+ f = fromMaybe [] . toLoc s+ g = fromMaybe (getId s) . fromLoc s+ rec (i:is) (j:js)+ | i == j = i : rec is js+ | otherwise = [j]+ rec _ _ = [] -- | Returns a list of all strategy locations, paired with the labeled --- substrategy or rule at that location--strategyLocations :: LabeledStrategy a -> [(StrategyLocation, StrategyOrRule a)]-strategyLocations = collect . addLocation . toCore . toStrategy- where- collect core = +-- substrategy at that location+strategyLocations :: LabeledStrategy a -> [([Int], LabeledStrategy a)]+strategyLocations s = ([], s) : rec [] (toCore (unlabel s))+ where + rec is = concat . zipWith make (map (:is) [0..]) . collect+ + make is (l, core) = + let ls = makeLabeledStrategy l (toStrategy core)+ in (is, ls) : rec is core+ + collect core = case core of- Label (loc, info) s -> - let this = makeLabeledStrategy info (mapLabel snd s)- in (loc, Left this) : collect s- Rule (Just (loc, _)) r -> - [(loc, Right r)]- _ -> - concatMap collect (children core)+ Label l t -> [(l, t)]+ Not _ -> []+ _ -> concatMap collect (children core) -- | Returns the substrategy or rule at a strategy location. Nothing -- indicates that the location is invalid-subStrategy :: StrategyLocation -> LabeledStrategy a -> Maybe (StrategyOrRule a)-subStrategy loc = lookup loc . strategyLocations - --- local helper functions that decorates interesting places with a --- strategy lcations (major rules, and labels)-addLocation :: Core l a -> Core (StrategyLocation, l) a-addLocation = flip evalState topLocation . mapCoreM forLabel forRule- where- forLabel l ma = do- loc <- get- put (downLocation loc)- rest <- ma- put (nextLocation loc)- return (Label (loc, l) rest)- forRule (Just l) r = do- loc <- get- put (nextLocation loc)- return (Rule (Just (loc, l)) r)- forRule Nothing r =- return (Rule Nothing r)+subStrategy :: Id -> LabeledStrategy a -> Maybe (LabeledStrategy a)+subStrategy loc = + fmap snd . safeHead . filter ((==loc) . getId . snd) . strategyLocations++fromLoc :: LabeledStrategy a -> [Int] -> Maybe Id+fromLoc s loc = fmap getId (lookup loc (strategyLocations s))++toLoc :: LabeledStrategy a -> Id -> Maybe [Int]+toLoc s i = + fmap fst (safeHead (filter ((==i) . getId . snd) (strategyLocations s)))
+ src/Common/Strategy/Parsing.hs view
@@ -0,0 +1,191 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- Basic machinery for executing a core strategy expression.+--+-----------------------------------------------------------------------------+module Common.Strategy.Parsing+ ( Step(..)+ , State, makeState, stack, choices, trace, value+ , parseDerivationTree, replay, runCore, runCoreWith+ ) where++import Common.Classes+import Common.Derivation+import Common.Strategy.Core+import Common.Transformation+import Control.Monad++----------------------------------------------------------------------+-- Step data type++data Step l a = Enter l | Exit l | RuleStep (Rule a)+ deriving Show++instance Apply (Step l) where+ applyAll (RuleStep r) = applyAll r+ applyAll _ = return++----------------------------------------------------------------------+-- State data type++data State l a = S+ { stack :: [Either l (Core l a)]+ , choices :: [Bool]+ , trace :: [Step l a]+ , value :: a+ }++makeState :: Core l a -> a -> State l a+makeState core a = push core (S [] [] [] a)++----------------------------------------------------------------------+-- Parse derivation tree++parseDerivationTree :: State l a -> DerivationTree (Step l a) (State l a)+parseDerivationTree state = addBranches list node+ where+ xs = firsts state+ empty = not (null [ () | (Ready, _) <- xs ])+ node = singleNode state empty+ list = [ (step, parseDerivationTree s) | (Result step, s) <- xs ] ++firsts :: State l a -> [(Result (Step l a), State l a)]+firsts st =+ case pop st of + Nothing -> [(Ready, st)]+ Just (Left l, s) -> [(Result (Exit l), traceExit l s)]+ Just (Right core, s) -> firstsStep core s+ where+ firstsStep core state =+ case core of+ a :*: b -> firstsStep a (push b state)+ a :|: b -> chooseFor True a ++ chooseFor False b+ Rec i a -> firstsStep (substCoreVar i core a) state+ Var _ -> freeCoreVar "firsts"+ Rule r -> hasStep (RuleStep r) (useRule r state)+ Label l a -> hasStep (Enter l) [push a (pushExit l state)]+ Not a -> guard (checkNot a state) >> firsts state+ a :|>: b -> firstsStep (coreOrElse a b) state+ Many a -> firstsStep (coreMany a) state+ Repeat a -> firstsStep (coreRepeat a) state+ Fail -> []+ Succeed -> firsts state+ where+ chooseFor b = flip firstsStep (makeChoice b state)+ hasStep step xs = [ (Result step, traceStep step s) | s <- xs ]++-- helper datatype+data Result a = Result a | Ready++----------------------------------------------------------------------+-- Running the parser++runCore :: Core l a -> a -> [a]+runCore core = runState . makeState core++runCoreWith :: CoreEnv l a -> Core l a -> a -> [a]+runCoreWith env = runCore . substCoreEnv env++runState :: State l a -> [a]+runState st =+ case pop st of+ Nothing -> [value st]+ Just (Left _, s) -> runState s+ Just (Right core, s) -> runStep core s+ where+ runStep core state = + case core of+ a :*: b -> runStep a (push b state)+ a :|: b -> runStep a state ++ runStep b state+ Rec i a -> runStep (substCoreVar i core a) state+ Var _ -> freeCoreVar "runState"+ Rule r -> concatMap runState (useRule r state)+ Label _ a -> runStep a state+ Not a -> guard (checkNot a state) >> runState state+ a :|>: b -> let xs = runStep a state+ in if null xs then runStep b state else xs+ Many a -> runStep (coreMany a) state+ Repeat a -> runStep (coreRepeat a) state+ Fail -> []+ Succeed -> runState state++----------------------------------------------------------------------+-- Replay a parse run++replay :: Monad m => Int -> [Bool] -> Core l a -> m (State l a)+replay n0 bs0 = replayState n0 bs0 . flip makeState noValue+ where+ noValue = error "no value in replay"+ + replayState n bs state = + case pop state of+ _ | n==0 -> return state+ Nothing -> return state+ Just (Left l, s) -> replayState (n-1) bs (traceExit l s)+ Just (Right core, s) -> replayStep n bs core s+ + replayStep n bs core state =+ case core of+ _ | n==0 -> return state+ a :*: b -> replayStep n bs a (push b state)+ a :|: b -> case bs of+ [] -> fail "replay failed"+ x:xs -> let new = if x then a else b+ in replayStep n xs new (makeChoice x state)+ Rec i a -> replayStep n bs (substCoreVar i core a) state+ Var _ -> freeCoreVar "replay"+ Rule r -> replayState (n-1) bs (traceRule r state)+ Label l a -> replayStep (n-1) bs a (pushExit l (traceEnter l state))+ Not _ -> replayState n bs state+ a :|>: b -> replayStep n bs (coreOrElse a b) state+ Many a -> replayStep n bs (coreMany a) state+ Repeat a -> replayStep n bs (coreRepeat a) state+ Fail -> fail "replay failed"+ Succeed -> replayState n bs state++----------------------------------------------------------------------+-- Local helper functions and instances+ +push :: Core l a -> State l a -> State l a+push core s = s {stack = Right core : stack s}++pushExit :: l -> State l a -> State l a+pushExit l s = s {stack = Left l : stack s}++pop :: State l a -> Maybe (Either l (Core l a), State l a)+pop s = case stack s of+ [] -> Nothing+ x:xs -> Just (x, s {stack = xs})+ +makeChoice :: Bool -> State l a -> State l a+makeChoice b s = s {choices = b : choices s}++checkNot :: Core l a -> State l a -> Bool+checkNot core = null . runCore core . value++useRule :: Rule a -> State l a -> [State l a]+useRule r state = [ state {value = b} | b <- applyAll r (value state) ]++traceEnter, traceExit :: l -> State l a -> State l a+traceEnter = traceStep . Enter+traceExit = traceStep . Exit++traceRule :: Rule a -> State l a -> State l a+traceRule = traceStep . RuleStep++traceStep :: Step l a -> State l a -> State l a+traceStep step s = s {trace = step : trace s}++substCoreVar :: Int -> Core l a -> Core l a -> Core l a+substCoreVar i a = substCoreEnv (insertCoreEnv i a emptyCoreEnv)++freeCoreVar :: String -> a+freeCoreVar caller = error $ "Free var in core expression: " ++ caller
src/Common/Strategy/Prefix.hs view
@@ -14,18 +14,16 @@ ----------------------------------------------------------------------------- module Common.Strategy.Prefix ( Prefix, emptyPrefix, makePrefix- , Step(..), prefixToSteps, prefixTree, stepsToRules, lastStepInPrefix+ , prefixToSteps, prefixTree, stepsToRules, lastStepInPrefix ) where -import Common.Apply import Common.Utils import Common.Strategy.Abstract-import Common.Strategy.Core+import Common.Strategy.Parsing import Common.Transformation import Common.Derivation-import Common.Strategy.Location-import Common.Strategy.BiasedChoice import Data.Maybe+import Control.Monad ----------------------------------------------------------- --- Prefixes@@ -34,13 +32,22 @@ -- executed rules). A prefix is still "aware" of the labels that appear in the -- strategy. A prefix is encoded as a list of integers (and can be reconstructed -- from such a list: see @makePrefix@). The list is stored in reversed order.-data Prefix a = P [(Int, Bias Step a)] (DerivationTree (Bias Step a) ())+data Prefix a = P (State LabelInfo a) +prefixPair :: Prefix a -> (Int, [Bool])+prefixPair (P s) = (length (trace s), reverse (choices s))++prefixIntList :: Prefix a -> [Int]+prefixIntList = f . prefixPair+ where+ f (0, []) = []+ f (n, bs) = n : map (\b -> if b then 0 else 1) bs+ instance Show (Prefix a) where- show (P xs _) = show (reverse (map fst xs))+ show = show . prefixIntList instance Eq (Prefix a) where- P xs _ == P ys _ = map fst xs == map fst ys+ a == b = prefixPair a == prefixPair b -- | Construct the empty prefix for a labeled strategy emptyPrefix :: LabeledStrategy a -> Prefix a@@ -48,60 +55,28 @@ -- | Construct a prefix for a given list of integers and a labeled strategy. makePrefix :: Monad m => [Int] -> LabeledStrategy a -> m (Prefix a)-makePrefix is ls = rec [] is start+makePrefix [] ls = makePrefix [0] ls+makePrefix (i:is) ls = liftM P $ + replay i (map (==0) is) (mkCore ls) where- mkCore = placeBiasLabels . processLabelInfo snd- . addLocation . toCore . toStrategy- start = strategyTree biasT (mkCore ls)- - rec acc [] t = return (P acc t)- rec acc (n:ns) t =- case drop n (branches t) of- (step, st):_ -> rec ((n, step):acc) ns st- _ -> fail ("invalid prefix: " ++ show is)-- biasT :: Translation (Either (Bias Step a) (StrategyLocation, LabelInfo)) a (Bias Step a)- biasT = (forLabel, Normal . Step)- - forLabel (Left bias) = Before bias- forLabel (Right (loc, i)) = Around (Normal (Begin loc i)) (Normal (End loc i))- --- | The @Step@ data type can be used to inspect the structure of the strategy-data Step a = Begin StrategyLocation LabelInfo- | Step (Rule a) - | End StrategyLocation LabelInfo- deriving Show--instance Apply Step where- applyAll (Step r) = applyAll r- applyAll (Begin _ _) = return- applyAll (End _ _) = return--instance Apply Prefix where- applyAll p = results . prefixTree p+ mkCore = processLabelInfo id . toCore . toStrategy -- | Create a derivation tree with a "prefix" as annotation. prefixTree :: Prefix a -> a -> DerivationTree (Prefix a) a-prefixTree (P xs t) = changeLabel snd . biasTreeG suc . runTree (decorate xs t)+prefixTree (P s) a = f (parseDerivationTree s {value = a}) where- suc t = endpoint t || any p (annotations t) || any suc (subtrees t)- p (Step r, _) = isMajorRule r- p _ = False- -decorate :: [(Int, Bias Step a)] -> DerivationTree (Bias Step a) () -> DerivationTree (Bias Step a) (Prefix a)-decorate xs t =- let list = zipWith make [0..] (branches t)- make i (s, st) = (s, decorate ((i,s):xs) st)- in addBranches list (singleNode (P xs t) (endpoint t))- --- | Returns the steps that belong to the prefix-prefixToSteps :: Prefix a -> [Step a]-prefixToSteps (P xs _) = [ step | (_, Normal step) <- reverse xs ]+ f t = addBranches list (singleNode (value $ root t) (endpoint t))+ where+ list = map g (branches t)+ g (_, st) = (P (root st), f st)++prefixToSteps :: Prefix a -> [Step LabelInfo a]+prefixToSteps (P t) = reverse (trace t) -- | Retrieves the rules from a list of steps-stepsToRules :: [Step a] -> [Rule a]-stepsToRules steps = [ r | Step r <- steps ]+stepsToRules :: [Step l a] -> [Rule a]+stepsToRules xs = [ r | RuleStep r <- xs ] -- | Returns the last rule of a prefix (if such a rule exists)-lastStepInPrefix :: Prefix a -> Maybe (Step a)-lastStepInPrefix (P xs _) = safeHead [ step | (_, Normal step) <- xs ]+lastStepInPrefix :: Prefix a -> Maybe (Step LabelInfo a)+lastStepInPrefix (P t) = safeHead (trace t)
+ src/Common/StringRef.hs view
@@ -0,0 +1,128 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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) +-- +-- References to Strings, proving a fast comparison implementation (Eq and +-- Ord) that uses a hash function. Code is based on Daan Leijen's Lazy +-- Virutal Machine (LVM) identifiers. +-- +----------------------------------------------------------------------------- +module Common.StringRef (StringRef, stringRef, toString) where + +import Data.Bits +import Data.IORef +import Data.List +import System.IO.Unsafe +import qualified Data.IntMap as IM + +---------------------------------------------------------------- +-- StringRef datatype and instance declarations + +data StringRef = S !Int + deriving (Eq, Ord) + +instance Show StringRef where + show s@(S i) = '#' : show i ++ toString s + +---------------------------------------------------------------- +-- Hash table + +type HashTable = IM.IntMap [String] + +tableRef :: IORef HashTable +tableRef = unsafePerformIO (newIORef IM.empty) + +---------------------------------------------------------------- +-- Conversion to and from strings + +stringRef :: String -> StringRef +stringRef s = unsafePerformIO $ do + let hash = hashString s + m <- readIORef tableRef + case IM.insertLookupWithKey (\_ -> combine) hash [s] m of + (Nothing, new) -> do + writeIORef tableRef new + return (S (encodeIndexZero hash)) + (Just old, new) -> + case findIndex (==s) old of + Just index -> + return (S (encode hash index)) + Nothing -> do + let index = length old + writeIORef tableRef new + return (S (encode hash index)) + +toString :: StringRef -> String +toString (S i) = unsafePerformIO $ do + m <- readIORef tableRef + case IM.lookup (extractHash i) m of + Just xs -> return (atIndex (extractIndex i) xs) + Nothing -> intErr "id not found" + +---------------------------------------------------------------- +-- Bit encoding + +encode :: Int -> Int -> Int +encode hash index = hash + index `shiftL` 12 + +encodeIndexZero :: Int -> Int +encodeIndexZero hash = hash + +extractHash :: Int -> Int +extractHash i = i `mod` 4096 + +extractIndex :: Int -> Int +extractIndex i = i `shiftR` 12 + +---------------------------------------------------------------- +-- Hash function + +-- simple hash function that performs quite good in practice +hashString :: String -> Int +hashString s = (f s `mod` prime) `mod` maxHash + where + f = foldl' next 0 -- ' strict fold + next n c = n*65599 + fromEnum c + prime = 32537 --65599 -- require: prime < maxHash + +maxHash :: Int +maxHash = 0xFFF -- 12 bits + +---------------------------------------------------------------- +-- Utility functions + +atIndex :: Int -> [a] -> a +atIndex 0 (x:_) = x +atIndex i (_:xs) = atIndex (i-1) xs +atIndex _ _ = intErr "corrupt symbol table" + +combine :: Eq a => [a] -> [a] -> [a] +combine [a] = rec + where + rec [] = [a] + rec this@(x:xs) + | a == x = this + | otherwise = x:rec xs +combine _ = intErr "combine" + +intErr :: String -> a +intErr s = error ("Internal error in Common.StringRef: " ++ s) + +---------------------------------------------------------------- +-- Testing and debugging + +{- +printTable :: IO () +printTable = readIORef tableRef >>= print + +test1 _ = toString (stringRef "bas") == "bas" +test2 _ = stringRef "bas" == stringRef "bas" +test3 _ = stringRef "bas" /= stringRef "je" +test4 _ = stringRef "arith1.unary_minus" /= stringRef "distribute power" +-}
+ src/Common/TestSuite.hs view
@@ -0,0 +1,385 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- A lightweight wrapper around the QuickCheck library. It introduces the+-- notion of a test suite, and it stores the test results for later inspection+-- (e.g., for the generation of a test report). A test suite has a monadic+-- interface.+--+-----------------------------------------------------------------------------+module Common.TestSuite + ( -- * Test Suite Monad+ TestSuite, MonadIO(..)+ -- * Test suite constructors+ , suite, addProperty, addPropertyWith+ , assertTrue, assertTrueMsg, assertNull, assertEquals+ , assertIO, warn+ -- * Running a test suite+ , runTestSuite, runTestSuiteResult+ -- * Test Suite Result+ , TestSuiteResult, subResults+ , makeSummary, printSummary+ , makeTestLog, makeTestLogWith, printTestLog+ -- * Formatting+ , FormatLog(..), formatLog, formatTimeDiff+ ) where++import Control.Arrow+import Data.List+import Data.Monoid+import qualified Data.Sequence as S+import Data.Foldable (toList)+import Test.QuickCheck+import Control.Monad.State+import System.Time hiding (formatTimeDiff)++----------------------------------------------------------------+-- Test Suite Monad++-- Integer corresponds to the number of characters on the current line, and+-- is used for formatting+newtype TestSuiteM a = TSM { unTSM :: M a }++type M a = StateT (Int, ResultTree) IO a+type TestSuite = TestSuiteM ()++instance Monad TestSuiteM where+ return = TSM . return+ m >>= f = TSM (unTSM m >>= unTSM . f)+ fail s = do assertTrueMsg "" s False+ return (error "TestSuite.fail: do not bind result")++instance MonadIO TestSuiteM where+ liftIO = TSM . liftIO++instance Monoid a => Monoid (TestSuiteM a) where+ mempty = return mempty+ mappend = (>>)++----------------------------------------------------------------+-- Test suite constructors++-- | Construct a (named) test suite containing tests and other suites+suite :: String -> TestSuite -> TestSuite+suite s m = TSM $ do+ newline+ liftIO $ putStrLn s+ reset+ (t, td) <- getDiff (withEmptyTree (unTSM m))+ addTree (labeled (s, td) t)++-- | Add a QuickCheck property to the test suite. The first argument is +-- a label for the property+addProperty :: Testable prop => String -> prop -> TestSuite+addProperty = flip addPropertyWith stdArgs++-- | Add a QuickCheck property to the test suite, also providing a test+-- configuration (Args)+addPropertyWith :: Testable prop => String -> Args -> prop -> TestSuite+addPropertyWith s args p = TSM $ do+ newlineIndent+ r <- liftIO $ quickCheckWithResult args p+ reset+ addResult s (toTestResult (maxSuccess args) r)++assertTrue :: String -> Bool -> TestSuite+assertTrue msg = assertIO msg . return++assertTrueMsg :: String -> String -> Bool -> TestSuite+assertTrueMsg s msg = addAssertion (Error msg) s . return++assertNull :: Show a => String -> [a] -> TestSuite+assertNull s xs = addAssertion (f xs) s (return (null xs))+ where f = Error . concat . intersperse "\n" . map show+ +assertEquals :: (Eq a, Show a) => String -> a -> a -> TestSuite+assertEquals s x y = addAssertion msg s (return (x==y))+ where msg = Error ("Not equal: " ++ show x ++ " and " ++ show y)++assertIO :: String -> IO Bool -> TestSuite+assertIO = addAssertion (Error "Assertion failed")++warn :: String -> TestSuite+warn msg = addAssertion (Warning msg) "" (return False)++-- local helpers+addAssertion :: TestResult -> String -> IO Bool -> TestSuite+addAssertion msg s io = TSM $ do+ b <- liftIO (io `catch` \_ -> return False)+ if b then do + dot+ addResult s (Ok 1)+ else do+ newlineIndent+ liftIO $ putStrLn (s ++ ": " ++ show msg)+ reset+ addResult s msg++addResult :: String -> TestResult -> M ()+addResult s r = addTree (single (s, r))++addTree :: ResultTree -> M ()+addTree t = modify (second (`mappend` t))++withEmptyTree :: M () -> M ResultTree+withEmptyTree m = do+ t0 <- gets snd+ modify (second (const mempty))+ m+ tr <- gets snd+ modify (second (const t0))+ return tr++-- formatting helpers+newline :: M ()+newline = do+ i <- gets fst+ when (i>0) (liftIO $ putChar '\n')+ reset++newlineIndent :: M ()+newlineIndent = do+ newline+ liftIO $ putStr " "+ modify (first (const 3))++dot :: M ()+dot = do+ i <- gets fst+ unless (i>0 && i<60) newlineIndent+ liftIO $ putChar '.'+ modify (first (+1))++reset :: M ()+reset = modify (first (const 0))++----------------------------------------------------------------+-- Running a test suite++runTestSuite :: TestSuite -> IO ()+runTestSuite s = runTestSuiteResult s >> return ()++runTestSuiteResult :: TestSuite -> IO TestSuiteResult+runTestSuiteResult s = liftM TSR $ getDiff $ liftM snd $+ execStateT (unTSM s >> newline) (0, mempty)++----------------------------------------------------------------+-- Test Suite Result++newtype TestSuiteResult = TSR (ResultTree, TimeDiff)++type ResultTree = Tree (String, TimeDiff) (String, TestResult)++data TestResult = Ok !Int | Error String | Warning String++instance Show TestResult where+ show (Ok _) = "Ok"+ show (Error msg) = "Error: " ++ msg+ show (Warning msg) = "Warning: " ++ msg++-- one-line summary+instance Show TestSuiteResult where+ show (TSR (tree, diff)) = + let (n, nf, nw) = collectInfo tree+ in "(tests: " ++ show n ++ ", failures: " ++ show nf +++ ", warnings: " ++ show nw ++ ", " ++ formatTimeDiff diff ++ ")"++subResults :: TestSuiteResult -> [(String, TestSuiteResult)]+subResults (TSR (tree, _)) = + let f ((s, diff), t) = (s, TSR (t, diff))+ in map f (subtrees tree)++printSummary :: TestSuiteResult -> IO ()+printSummary = putStrLn . makeSummary++makeSummary :: TestSuiteResult -> String+makeSummary result@(TSR (tree, diff)) = unlines $+ [ line+ , "Tests : " ++ show n+ , "Failures : " ++ show nf+ , "Warnings : " ++ show nw+ , "\nTime : " ++ formatTimeDiff diff+ , "\nSuites: "+ ] ++ map f (subResults result) + ++ [line]+ where+ line = replicate 75 '-'+ (n, nf, nw) = collectInfo tree+ f (name, r) = " " ++ name ++ " " ++ show r++printTestLog :: TestSuiteResult -> IO ()+printTestLog = putStrLn . makeTestLog++makeTestLog :: TestSuiteResult -> String+makeTestLog = unlines . makeTestLogWith formatLog++makeTestLogWith :: Monoid a => FormatLog a -> TestSuiteResult -> a+makeTestLogWith fm (TSR (tree, diff)) = formatRoot fm diff (make [] tree)+ where+ make loc = mconcat . map (either forTests forSuite) . treeToList+ where+ treeToList = + let op (i, ys) y = + case y of + Left b -> (i, Left b:ys)+ Right p -> (i+1, Right (loc ++ [i], p):ys)+ in reverse . snd . foldl op (1, []) . collectLevel++ forSuite (nl, ((s, d), t)) = + formatSuite fm nl s (collectInfo t) d (make nl t)+ + forTests [] = mempty+ forTests list@((s, result) : rest) = + case result of + Warning msg -> next (formatWarning fm s msg)+ Error msg -> next (formatFailure fm s msg)+ Ok _ ->+ let (ys, zs) = span (isOk . snd) list+ sucs = [ (x, n) | (x, Ok n) <- ys ]+ in formatSuccesses fm sucs `mappend` forTests zs+ where+ next a = a `mappend` forTests rest++data FormatLog a = FormatLog+ { formatRoot :: TimeDiff -> a -> a+ , formatSuite :: [Int] -> String -> (Int, Int, Int) -> TimeDiff -> a -> a+ , formatSuccesses :: [(String, Int)] -> a+ , formatFailure :: String -> String -> a+ , formatWarning :: String -> String -> a+ }++formatLog :: FormatLog [String]+formatLog = FormatLog+ { formatRoot = \td a -> + a ++ ["\n(Total time: " ++ formatTimeDiff td ++ ")"]+ , formatSuite = \loc s _ td a -> + [showLoc loc ++ ". " ++ s] ++ a ++ + [" (" ++ formatTimeDiff td ++ " for " ++ s ++ ")"]+ , formatSuccesses = \xs -> + let f (_, n) = if n==1 then "." else "(" ++ show n ++ " tests)"+ in [" " ++ concatMap f xs]+ , formatFailure = \s msg ->+ [" " ++ putLabel s ++ "Error: " ++ msg]+ , formatWarning = \s msg ->+ [" " ++ putLabel s ++ "Warning: " ++ msg]+ }+ where + putLabel s = if null s then "" else s ++ ": "++formatTimeDiff :: TimeDiff -> String+formatTimeDiff d@(TimeDiff z1 z2 z3 h m s p)+ | any (/=0) [z1,z2,z3] = timeDiffToString d+ | s >= 60 = formatTimeDiff (timeDiff ((h*60+m)*60+s) p)+ | h==0 && m==0 = show inSec ++ " secs"+ | otherwise = show (60*h+m) ++ ":" ++ digSec ++ " mins" + where+ milSec = 1000*toInteger s + p `div` 1000000000+ inSec = fromIntegral milSec / 1000 :: Double+ digSec = (if s < 10 then ('0' :) else id) (show s)+ timeDiff n pc = + let (rest, sn) = n `divMod` 60+ (hr, mr) = rest `divMod` 60+ in TimeDiff 0 0 0 hr mr sn pc++-----------------------------------------------------+-- Utility functions++-- A sequence of leafs (Left) or labeled items (Right)+newtype Tree a b = T { unT :: S.Seq (Either b (a, Tree a b)) }++instance Monoid (Tree a b) where+ mempty = T mempty+ mappend (T a) (T b) = T (mappend a b)+ +single :: b -> Tree a b+single = T . S.singleton . Left++labeled :: a -> Tree a b -> Tree a b+labeled a t = T (S.singleton (Right (a, t)))+ +toTestResult :: Int -> Result -> TestResult+toTestResult n result = + case result of+ Success _ -> Ok n+ Failure _ _ msg _ -> Error msg+ NoExpectedFailure _ -> Error "no expected failure"+ GaveUp i _ -> Warning ("passed only " ++ show i ++ " tests")+ +showLoc :: [Int] -> String+showLoc = concat . intersperse "." . map show++collectInfo :: Tree a (String, TestResult) -> (Int, Int, Int)+collectInfo tree = (length tests, length failures, length warnings)+ where+ tests = flatten tree+ failures = [ msg | (_, Error msg) <- tests ]+ warnings = [ msg | (_, Warning msg) <- tests ]++isOk :: TestResult -> Bool+isOk (Ok _) = True+isOk _ = False++subtrees :: Tree a b -> [(a, Tree a b)]+subtrees t = [ p | Right p <- collectLevel t ]++flatten :: Tree a b -> [b]+flatten t = [ b | x <- collectLevel t, b <- either id (flatten . snd) x ]++collectLevel :: Tree a b -> [Either [b] (a, Tree a b)]+collectLevel = combine [] . toList . unT+ where+ combine acc [] = f acc+ combine acc (Left a:rest) = combine (a:acc) rest+ combine acc (Right b:rest) = f acc ++ (Right b : combine [] rest)+ + f acc = [ Left (reverse acc) | not (null acc) ] ++getDiff :: MonadIO m => m a -> m (a, TimeDiff)+getDiff action = do+ t0 <- liftIO getClockTime+ a <- action+ t1 <- liftIO getClockTime+ return (a, diffClockTimes t1 t0)++-- Example+{-+main :: IO ()+main = do+ r <- runTestSuiteResult $ do+ suite "A" $ do+ addProperty "p1" p1+ addProperty "p1" p1+ suite "A1" $ addProperty "p2" p2+ suite "A2" $ return ()+ addProperty "p3" p3+ suite "B" $ do+ addProperty "p4" p4+ addProperty "W" (\xs -> length (xs::[Int]) > 100 ==> True)+ suite "C" $ do+ addProperty "p5" p5+ assertTrue "sorted" (sort [3,2,1] == [1,2,3])+ fail "This is a failure"+ warn "This is a warning"+ assertEquals "eq" (sort [1,2,2]) (nub [1,2,2]) + assertTrue "yes" True+ + printSummary r+ printTestLog r+ --print r+ --print (subResults r)+ where + p1 xs = sort (xs::[Int]) == sort (sort xs)+ p2 xs = reverse (reverse xs) == (xs::[Int])+ p3 xs = head (sort xs) == minimum (xs::[Int])+ p4 xs = sort (nub xs) == nub (sort (xs::[Int]))+ p5 xs = reverse (sort xs) == sort (reverse (xs :: [Int]))++main = runTestSuite $ suite "A" $ assertIO "B" (return True) >> + assertIO "D" (fail "boe") >> assertIO "C" (return True) -}
src/Common/Transformation.hs view
@@ -17,29 +17,27 @@ ----------------------------------------------------------------------------- module Common.Transformation ( -- * Transformations- Transformation(RewriteRule), makeTrans, makeTransList+ Transformation, makeTrans, makeTransList, makeRewriteTrans -- * Arguments , ArgDescr(..), defaultArgDescr, Argument(..) , supply1, supply2, supply3, supplyLabeled1, supplyLabeled2, supplyLabeled3, supplyWith1 , hasArguments, expectedArguments, getDescriptors, useArguments -- * Rules- , Rule, name, isMinorRule, isMajorRule, isBuggyRule, isRewriteRule- , ruleGroups, ruleDescription, ruleSiblings, addRuleToGroup, describe- , rule, ruleList, ruleListF+ , Rule, isMinorRule, isMajorRule, isBuggyRule, isRewriteRule+ , ruleGroups, ruleSiblings, addRuleToGroup+ , rule, ruleList , makeRule, makeRuleList, makeSimpleRule, makeSimpleRuleList , idRule, checkRule, emptyRule, minorRule, buggyRule, doBefore, doAfter- , transformations, getRewriteRules, doBeforeTrans+ , siblingOf, transformations, getRewriteRules, doBeforeTrans+ , ruleRecognizer, useRecognizer -- * Lifting- , ruleOnce, ruleOnce2, ruleMulti, ruleMulti2, ruleSomewhere , liftRule, liftTrans, liftRuleIn, liftTransIn -- * QuickCheck- , testRule, testRuleSmart+ , testRule, propRuleSmart ) where -import Common.Apply import Common.Rewriting-import Common.Traversable-import Common.Uniplate (Uniplate, somewhereM)+import Common.Classes import Common.Utils import Common.View import Control.Monad@@ -47,6 +45,7 @@ import Data.Maybe import Data.Ratio import Test.QuickCheck+import Common.Id ----------------------------------------------------------- --- Transformations@@ -54,17 +53,19 @@ -- | Abstract data type for representing transformations data Transformation a = Function (a -> [a])- | RewriteRule (RewriteRule a)+ | RewriteRule (RewriteRule a) (a -> [a]) | Transformation a :*: Transformation a -- sequence | forall b . Abstraction (ArgumentList b) (a -> Maybe b) (b -> Transformation a) | forall b c . LiftView (ViewList a (b, c)) (Transformation b)+ | Recognizer (a -> a -> Bool) (Transformation a) instance Apply Transformation where applyAll (Function f) = f- applyAll (RewriteRule r) = rewriteM r+ applyAll (RewriteRule _ f) = f applyAll (Abstraction _ f g) = \a -> maybe [] (\b -> applyAll (g b) a) (f a) applyAll (LiftView v t) = \a -> [ build v (b, c) | (b0, c) <- match v a, b <- applyAll t b0 ] applyAll (s :*: t) = \a -> applyAll s a >>= applyAll t+ applyAll (Recognizer _ t) = applyAll t -- | Turn a function (which returns its result in the Maybe monad) into a transformation makeTrans :: (a -> Maybe a) -> Transformation a@@ -74,6 +75,10 @@ makeTransList :: (a -> [a]) -> Transformation a makeTransList = Function +-- | Turn a rewrite rule into a transformation+makeRewriteTrans :: RewriteRule a -> Transformation a+makeRewriteTrans r = RewriteRule r (rewriteM r)+ ----------------------------------------------------------- --- Arguments @@ -158,8 +163,8 @@ -- | Returns a list of argument descriptors getDescriptors :: Rule a -> [Some ArgDescr]-getDescriptors rule =- case transformations rule of+getDescriptors r =+ case transformations r of [t] -> rec t _ -> [] where @@ -167,16 +172,17 @@ rec trans = case trans of Abstraction args _ _ -> someArguments args- LiftView _ t -> rec t- s :*: t -> rec s ++ rec t+ LiftView _ t -> rec t+ Recognizer _ t -> rec t+ s :*: t -> rec s ++ rec t _ -> [] -- | Returns a list of pretty-printed expected arguments. Nothing indicates that there are no such arguments expectedArguments :: Rule a -> a -> Maybe [String]-expectedArguments rule a =- case transformations rule of- [t] -> rec t a- _ -> Nothing+expectedArguments r =+ case transformations r of+ [t] -> rec t+ _ -> const Nothing where rec :: Transformation a -> a -> Maybe [String] rec trans a = @@ -188,15 +194,17 @@ rec t b s :*: t -> rec s a `mplus` rec t a+ Recognizer _ t ->+ rec t a _ -> Nothing -- | Transform a rule and use a list of pretty-printed arguments. Nothing indicates that the arguments are -- invalid (not parsable), or that the wrong number of arguments was supplied useArguments :: [String] -> Rule a -> Maybe (Rule a)-useArguments list rule =- case transformations rule of+useArguments list r =+ case transformations r of [t] -> do new <- make t- return rule {transformations = [new]}+ return r {transformations = [new]} _ -> Nothing where make :: Transformation a -> Maybe (Transformation a)@@ -204,6 +212,7 @@ case trans of Abstraction args _ g -> fmap g (parseArguments args list) LiftView v t -> fmap (LiftView v) (make t)+ Recognizer f t -> fmap (Recognizer f) (make t) s :*: t -> fmap (:*: t) (make s) `mplus` fmap (s :*:) (make t) _ -> Nothing@@ -246,8 +255,8 @@ where showRatio r = show (numerator r) ++ if denominator r == 1 then "" else '/' : show (denominator r) parseRatio s = - let readDivOp s = - case dropWhile isSpace s of+ let readDivOp t = + case dropWhile isSpace t of ('/':rest) -> return rest [] -> return "1" _ -> fail "no (/) operator" @@ -264,26 +273,29 @@ -- | Abstract data type for representing rules data Rule a = Rule - { name :: String -- ^ Returns the name of the rule (should be unique)- , ruleDescription :: String -- ^ A short description what the rule is doing+ { ruleId :: Id -- ^ Unique identifier of the rule , transformations :: [Transformation a] , isBuggyRule :: Bool -- ^ Inspect whether or not the rule is buggy (unsound) , isMinorRule :: Bool -- ^ Returns whether or not the rule is minor (i.e., an administrative step that is automatically performed by the system)- , ruleGroups :: [String]- , ruleSiblings :: [String]+ , ruleGroups :: [Id]+ , ruleSiblings :: [Id] } instance Show (Rule a) where- show = name+ show = showId instance Eq (Rule a) where- r1 == r2 = name r1 == name r2+ r1 == r2 = ruleId r1 == ruleId r2 instance Apply Rule where applyAll r a = do t <- transformations r applyAll t a +instance HasId (Rule a) where+ getId = ruleId+ changeId f r = r { ruleId = f (ruleId r) } + -- | Returns whether or not the rule is major (i.e., not minor) isMajorRule :: Rule a -> Bool isMajorRule = not . isMinorRule@@ -291,35 +303,34 @@ isRewriteRule :: Rule a -> Bool isRewriteRule = not . null . getRewriteRules -describe :: String -> Rule a -> Rule a-describe txt r = r { ruleDescription = txt ++ "\n" ++ ruleDescription r}--addRuleToGroup :: String -> Rule a -> Rule a-addRuleToGroup group r = r { ruleGroups = group : ruleGroups r }--ruleList :: (Builder f a, Rewrite a) => String -> [f] -> Rule a-ruleList s = makeRuleList s . map (RewriteRule . rewriteRule s)+siblingOf :: HasId b => b -> Rule a -> Rule a +siblingOf sib r = r { ruleSiblings = getId sib : ruleSiblings r } -ruleListF :: (BuilderList f a, Rewrite a) => String -> f -> Rule a-ruleListF s = makeRuleList s . map RewriteRule . rewriteRules s+addRuleToGroup :: HasId b => b -> Rule a -> Rule a+addRuleToGroup g r = r { ruleGroups = getId g : ruleGroups r } -rule :: (Builder f a, Rewrite a) => String -> f -> Rule a-rule s = makeRule s . RewriteRule . rewriteRule s+ruleList :: (IsId n, RuleBuilder f a, Rewrite a) => n -> [f] -> Rule a+ruleList n = makeRuleList a . map (makeRewriteTrans . rewriteRule a)+ where a = newId n+ +rule :: (IsId n, RuleBuilder f a, Rewrite a) => n -> f -> Rule a+rule n = makeRule a . makeRewriteTrans . rewriteRule a+ where a = newId n -- | Turn a transformation into a rule: the first argument is the rule's name-makeRule :: String -> Transformation a -> Rule a+makeRule :: IsId n => n -> Transformation a -> Rule a makeRule n = makeRuleList n . return -- | Turn a list of transformations into a single rule: the first argument is the rule's name-makeRuleList :: String -> [Transformation a] -> Rule a-makeRuleList n ts = Rule n [] ts False False [] []+makeRuleList :: IsId n => n -> [Transformation a] -> Rule a+makeRuleList n ts = Rule (newId n) ts False False [] [] -- | Turn a function (which returns its result in the Maybe monad) into a rule: the first argument is the rule's name-makeSimpleRule :: String -> (a -> Maybe a) -> Rule a+makeSimpleRule :: IsId n => n -> (a -> Maybe a) -> Rule a makeSimpleRule n = makeRule n . makeTrans -- | Turn a function (which returns a list of results) into a rule: the first argument is the rule's name-makeSimpleRuleList :: String -> (a -> [a]) -> Rule a+makeSimpleRuleList :: IsId n => n -> (a -> [a]) -> Rule a makeSimpleRuleList n = makeRule n . makeTransList -- | A special (minor) rule that always returns the identity@@ -363,76 +374,65 @@ f :: Transformation a -> [(Some RewriteRule, Bool)] f trans = case trans of- RewriteRule rr -> [(Some rr, not $ isBuggyRule r)] - LiftView _ t -> f t- s :*: t -> f s ++ f t- _ -> []----------------------------------------------------------------- Lifting---- | Lift a rule using the Once type class-ruleOnce :: Once f => Rule a -> Rule (f a)-ruleOnce r = makeSimpleRuleList (name r) $ onceM $ applyAll r---- | Apply a rule once (in two functors)-ruleOnce2 :: (Once f, Once g) => Rule a -> Rule (f (g a))-ruleOnce2 = ruleOnce . ruleOnce+ RewriteRule rr _ -> [(Some rr, not $ isBuggyRule r)] + LiftView _ t -> f t+ s :*: t -> f s ++ f t+ _ -> [] --- | Apply at multiple locations, but at least once-ruleMulti :: (Switch f, Crush f) => Rule a -> Rule (f a)-ruleMulti r = makeSimpleRuleList (name r) $ multi $ applyAll r+ruleRecognizer :: (a -> a -> Bool) -> Rule a -> a -> a -> Bool+ruleRecognizer eq r a b = or + [ transRecognizer eq t a b | t <- transformations r ] --- | Apply at multiple locations, but at least once (in two functors)-ruleMulti2 :: (Switch f, Crush f, Switch g, Crush g) => Rule a -> Rule (f (g a))-ruleMulti2 = ruleMulti . ruleMulti+transRecognizer :: (a -> a -> Bool) -> Transformation a -> a -> a -> Bool+transRecognizer eq trans a b =+ case trans of+ Recognizer f t -> f a b || transRecognizer eq t a b+ LiftView v t -> + any (`eq` b) (applyAll trans a) || or -- ?? Quick Fix+ [ transRecognizer (\x y -> eq (f x) (f y)) t av bv+ | (av, c) <- match v a + , (bv, _) <- match v b+ , let f z = build v (z, c)+ ]+ _ -> any (`eq` b) (applyAll trans a) -multi :: (Switch f, Crush f) => (a -> [a]) -> f a -> [f a]-multi f a =- let g a = case f a of - [] -> [(False, a)]- xs -> zip (repeat True) xs- xs = switch (fmap g a)- p = any fst . crush- in map (fmap snd) (filter p xs)+useRecognizer :: (a -> a -> Bool) -> Transformation a -> Transformation a+useRecognizer = Recognizer -ruleSomewhere :: Uniplate a => Rule a -> Rule a-ruleSomewhere r = makeSimpleRuleList (name r) $ somewhereM $ applyAll r+-----------------------------------------------------------+--- Lifting liftTrans :: View a b -> Transformation b -> Transformation a liftTrans v = liftTransIn (v &&& identity) -liftTransIn :: Crush m => ViewM m a (b, c) -> Transformation b -> Transformation a+liftTransIn :: (Crush m, Monad m) => ViewM m a (b, c) -> Transformation b -> Transformation a liftTransIn = LiftView . viewList liftRule :: View a b -> Rule b -> Rule a liftRule v = liftRuleIn (v &&& identity) -liftRuleIn :: Crush m => ViewM m a (b, c) -> Rule b -> Rule a+liftRuleIn :: (Crush m, Monad m) => ViewM m a (b, c) -> Rule b -> Rule a liftRuleIn v r = r- { transformations = map (liftTransIn v) (transformations r)- }+ { transformations = map (liftTransIn v) (transformations r) } ----------------------------------------------------------- --- QuickCheck -- | Check the soundness of a rule: the equality function is passed explicitly testRule :: (Arbitrary a, Show a) => (a -> a -> Bool) -> Rule a -> IO ()-testRule eq rule = - quickCheck (propRule eq rule arbitrary)+testRule eq r = + quickCheck (propRule eq r arbitrary) -- | Check the soundness of a rule and use a "smart generator" for this. The smart generator -- behaves differently on transformations constructed with a (|-), and for these transformations, -- the left-hand side patterns are used (meta variables are instantiated with random terms)-testRuleSmart :: Show a => (a -> a -> Bool) -> Rule a -> Gen a -> IO ()-testRuleSmart eq rule gen =- let cfg = stdArgs {maxSize = 10, maxSuccess = 10, maxDiscard = 100}- in quickCheckWith cfg (propRule eq rule (smartGen rule gen))+propRuleSmart :: Show a => (a -> a -> Bool) -> Rule a -> Gen a -> Property+propRuleSmart eq r = propRule eq r . smartGen r propRule :: Show a => (a -> a -> Bool) -> Rule a -> Gen a -> Property-propRule eq rule gen = +propRule eq r gen = forAll gen $ \a -> - forAll (smartApplyRule rule a) $ \ma -> + forAll (smartApplyRule r a) $ \ma -> isJust ma ==> (a `eq` fromJust ma) smartGen :: Rule a -> Gen a -> Gen a@@ -444,7 +444,7 @@ smartGenTrans :: a -> Transformation a -> [Gen a] smartGenTrans a trans = case trans of- RewriteRule r -> return (smartGenerator r)+ RewriteRule r _ -> return (smartGenerator r) LiftView v t -> do (b, c) <- match v a gen <- smartGenTrans b t
− src/Common/Traversable.hs
@@ -1,135 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Common.Traversable - ( Once(..), Switch(..), Crush(..), OnceJoin(..), useOnceJoin- ) where--import Control.Monad.Identity-import qualified Data.IntMap as IM-import qualified Data.Map as M--{- Examples:--once (^2) [1..3]- ~> [[1,2,3],[1,4,3],[1,2,9]]--onceM (\x -> [x+1, x^2]) [1..3]- ~> [[2,2,3],[1,2,3],[1,3,3],[1,4,3],[1,2,4],[1,2,9]]--onceJoin (\x -> [x+1, x^2]) [1..3]- ~> [[2,1,2,3],[1,3,4,3],[1,2,4,9]]--onceJoinM (\x -> [[x+1], [x^2, x^3]]) [1..3]- ~> [[2,2,3],[1,1,2,3],[1,3,3],[1,4,8,3],[1,2,4],[1,2,9,27]]--}---------------------------------------------------------------- * Type class |Once|--class Functor f => Once f where- -- | Apply a function once in a given structure- once :: (a -> a) -> f a -> [f a]- -- | Apply a monadic function once in a given structure- onceM :: MonadPlus m => (a -> m a) -> f a -> m (f a)- - -- default definition- once f = onceM (return . f)--instance Once [] where- onceM = useOnceJoin- -instance Once Maybe where- onceM = useOnceJoin- -instance Once Identity where- onceM = useOnceJoin--instance Eq a => Once (M.Map a) where- onceM f m = liftM M.fromAscList (onceM g (M.toList m))- where g (a, b) = liftM (\c -> (a, c)) (f b)--instance Once IM.IntMap where- onceM f m = liftM IM.fromAscList (onceM g (IM.toList m))- where g (a, b) = liftM (\c -> (a, c)) (f b)--useOnceJoin :: (OnceJoin f, MonadPlus m) => (a -> m a) -> f a -> m (f a)-useOnceJoin f = onceJoinM (liftM return . f)---------------------------------------------------------------- * Type class |Switch|--class Functor f => Switch f where- switch :: Monad m => f (m a) -> m (f a)- -instance Switch [] where- switch = sequence--instance Switch Maybe where- switch = maybe (return Nothing) (liftM Just)--instance Switch Identity where- switch (Identity m) = liftM Identity m--instance Eq a => Switch (M.Map a) where- switch m = do- let (ns, ms) = unzip (M.toList m)- as <- sequence ms - return $ M.fromAscList $ zip ns as--instance Switch IM.IntMap where- switch m = do- let (ns, ms) = unzip (IM.toList m)- as <- sequence ms - return $ IM.fromAscList $ zip ns as---------------------------------------------------------------- * Type class |Crush|--class Functor f => Crush f where- crush :: f a -> [a]--instance Crush [] where- crush = id--instance Crush Maybe where- crush = maybe [] return--instance Crush Identity where- crush = return . runIdentity--instance Crush (M.Map a) where- crush = M.elems--instance Crush IM.IntMap where- crush = IM.elems---------------------------------------------------------------- * Type class |OnceJoin|--class (Once f, Monad f) => OnceJoin f where- -- | Apply a function once in a given structure, join the result afterwards- onceJoin :: (a -> f a) -> f a -> [f a]- -- | Apply a monadic function once in a given structure, join the result afterwards- onceJoinM :: MonadPlus m => (a -> m (f a)) -> f a -> m (f a)-- -- default definition- onceJoin f = onceJoinM (return . f)--instance OnceJoin [] where- onceJoinM _ [] = mzero - onceJoinM f (x:xs) = liftM (++xs) (f x) `mplus` liftM (x:) (onceJoinM f xs)--instance OnceJoin Maybe where- onceJoinM = maybe mzero- -instance OnceJoin Identity where- onceJoinM f = f . runIdentity
src/Common/Uniplate.hs view
@@ -8,26 +8,30 @@ -- Stability : provisional -- Portability : portable (depends on ghc) ----- This module defines the Uniplate type class, and some utility functions. It--- should be replaced in future by the original Uniplate library.+-- Exports a subset of Data.Generics.Uniplate -- ------------------------------------------------------------------------------module Common.Uniplate (- -- * Uniplate type class and utility functions- Uniplate(..)- , universe, subtermsAt, children, child- , getTermAt, applyTo, applyToM, applyAt, applyAtM- , transform, transformM, transformTD, rewrite, rewriteM- , somewhere, somewhereM- , compos+module Common.Uniplate+ ( -- * Uniplate type class and utility functions+ Uniplate(..), universe, children, holes+ , transform, transformM, descend, descendM, rewrite, rewriteM+ -- * Additional functions+ , leafs ) where- ++import Data.Generics.Uniplate++leafs :: Uniplate a => a -> [a]+leafs a = case children a of+ [] -> [a]+ xs -> concatMap leafs xs++{- --------------------------------------------------------- -- Uniplate class for generic traversals import Common.Utils (safeHead) import Control.Monad- -- | The Uniplate type class offers some light-weight functions for generic traversals. Only -- a minimal set of operations are supported class Uniplate a where@@ -37,21 +41,50 @@ universe :: Uniplate a => a -> [a] universe a = a : [ c | b <- children a, c <- universe b ] --- | Like universe, but also returns the location of the subterm-subtermsAt :: Uniplate a => a -> [([Int], a)]-subtermsAt a = ([], a) : [ (i:is, b) | (i, c) <- zip [0..] (children a), (is, b) <- subtermsAt c ]- -- | Returns all the immediate children of a term children :: Uniplate a => a -> [a] children = fst . uniplate --- | Selects one immediate child of a term. Nothing indicates that the child does not exist-child :: Uniplate a => Int -> a -> Maybe a-child n = safeHead . drop n . children - +-- | A bottom-up transformation+transform :: Uniplate a => (a -> a) -> a -> a+transform g a = g $ f $ map (transform g) cs+ where+ (cs, f) = uniplate a++-- | Monadic variant of transform+transformM :: (Monad m, Uniplate a) => (a -> m a) -> a -> m a+transformM g a = mapM (transformM g) cs >>= (g . f)+ where+ (cs, f) = uniplate a++-- | Applies a function to its immediate children+descend :: Uniplate a => (a -> a) -> a -> a+descend g a = + let (cs, f) = uniplate a+ in f (map g cs)++-- | Applies the function at a position until this is no longer possible+rewrite :: Uniplate a => (a -> Maybe a) -> a -> a+rewrite f = transform g+ where g x = maybe x (rewrite f) (f x)++-- | Monadic variant of rewrite+rewriteM :: (Monad m, Uniplate a) => (a -> m (Maybe a)) -> a -> m a+rewriteM f = transformM g+ where g x = f x >>= maybe (return x) (rewriteM f)++---------------------------------------------------------+-- Additional functions++-- | Like universe, but also returns the location of the subterm+subtermsAt :: Uniplate a => a -> [([Int], a)]+subtermsAt a = ([], a) : [ (i:is, b) | (i, c) <- zip [0..] (children a), (is, b) <- subtermsAt c ]+ -- | Selects a child based on a path. Nothing indicates that the path is invalid getTermAt :: Uniplate a => [Int] -> a -> Maybe a getTermAt is a = foldM (flip child) a is+ where+ child n = safeHead . drop n . children -- | Apply a function to one immediate child. applyTo :: Uniplate a => Int -> (a -> a) -> a -> a@@ -75,34 +108,6 @@ applyAtM :: (Monad m, Uniplate a) => [Int] -> (a -> m a) -> a -> m a applyAtM is f = foldr applyToM f is --- | A bottom-up transformation-transform :: Uniplate a => (a -> a) -> a -> a-transform g a = g $ f $ map (transform g) cs- where- (cs, f) = uniplate a---- | Monadic variant of transform-transformM :: (Monad m, Uniplate a) => (a -> m a) -> a -> m a-transformM g a = mapM (transformM g) cs >>= (g . f)- where- (cs, f) = uniplate a---- | A top-down transformation-transformTD :: Uniplate a => (a -> a) -> a -> a-transformTD g a = - let (cs, f) = uniplate (g a)- in f (map (transformTD g) cs)- --- | Applies the function at a position until this is no longer possible-rewrite :: Uniplate a => (a -> Maybe a) -> a -> a-rewrite f = transform g- where g x = maybe x (rewrite f) (f x)---- | Monadic variant of rewrite-rewriteM :: (Monad m, Uniplate a) => (a -> m (Maybe a)) -> a -> m a-rewriteM f = transformM g- where g x = f x >>= maybe (return x) (rewriteM f)- somewhere :: Uniplate a => (a -> a) -> a -> [a] somewhere f = somewhereM (return . f) @@ -111,7 +116,4 @@ where n = length (children a) g i = applyToM i (somewhereM f) a---- | The compos function-compos :: Uniplate b => a -> (a -> a -> a) -> (b -> a) -> b -> a-compos zero combine f = foldr (combine . f) zero . children+-}
src/Common/Utils.hs view
@@ -20,7 +20,6 @@ import Data.Ratio import System.Random import Test.QuickCheck-import qualified Data.Map as M data Some f = forall a . Some (f a) @@ -30,9 +29,6 @@ instance Show ShowString where show = fromShowString -thoroughCheck :: Testable a => a -> IO ()-thoroughCheck = quickCheckWith $ stdArgs {maxSize = 500, maxSuccess = 500}- readInt :: String -> Maybe Int readInt xs | null xs = Nothing@@ -47,7 +43,7 @@ stringToHex :: String -> Maybe Int stringToHex = foldl op (Just 0) where- op (Just i) c = fmap (\j -> i*16 + j) (charToHex c)+ op (Just i) c = fmap (i*16+) (charToHex c) op Nothing _ = Nothing charToHex :: Char -> Maybe Int@@ -70,6 +66,10 @@ distinct [] = True distinct (x:xs) = all (/=x) xs && distinct xs +allsame :: Eq a => [a] -> Bool+allsame [] = True+allsame (x:xs) = all (==x) xs+ safeHead :: [a] -> Maybe a safeHead (x:_) = return x safeHead _ = Nothing@@ -99,56 +99,30 @@ Just (xs, ys) -> xs : splitsWithElem c ys Nothing -> [s] +splitAtSequence :: Eq a => [a] -> [a] -> Maybe ([a], [a])+splitAtSequence cs = f []+ where+ f _ [] = Nothing+ f acc list@(x:xs)+ | cs `isPrefixOf` list = Just (reverse acc, drop (length cs) list)+ | otherwise = f (x:acc) xs+ -- | Use a fixed standard "random" number generator. This generator is -- accessible by calling System.Random.getStdGen useFixedStdGen :: IO () useFixedStdGen = setStdGen (mkStdGen 280578) {- magic number -} +fst3 :: (a, b, c) -> a fst3 (x, _, _) = x++snd3 :: (a, b, c) -> b snd3 (_, x, _) = x++thd3 :: (a, b, c) -> c thd3 (_, _, x) = x commaList :: [String] -> String commaList = concat . intersperse ", "--primes :: [Int]-primes = rec [2..]- where- rec [] = error "Common.Utils: empty list"- rec (x:xs) = x : rec (filter (\y -> y `mod` x /= 0) xs)- -putLabel :: String -> IO ()-putLabel s = - let n = (40 - length s) `max` 3- in putStr (s ++ replicate n ' ')--reportTest :: String -> Bool -> IO ()-reportTest s b = putLabel s >> putStrLn (if b then "OK" else "FAILED")--instance Show (a -> b) where- show _ = "<function>"--{--instance Arbitrary Char where- arbitrary = let chars = ['a' .. 'z'] ++ ['A' .. 'Z']- in oneof (map return chars)-instance CoArbitrary Char where- coarbitrary = coarbitrary . ord--}--instance (Ord k, Arbitrary k, Arbitrary a) => Arbitrary (M.Map k a) where- arbitrary = liftM M.fromList arbitrary-instance (Ord k, CoArbitrary k, CoArbitrary a) => CoArbitrary (M.Map k a) where- coarbitrary = coarbitrary . M.toList--{---- Generating arbitrary random rational numbers-instance Integral a => Arbitrary (Ratio a) where- arbitrary = sized (\n -> ratioGen n (n `div` 4))-instance Integral a => CoArbitrary (Ratio a) where- coarbitrary r = f (numerator r) . f (denominator r)- where f = variant . fromIntegral--} -- | Prevents a bias towards small numbers ratioGen :: Integral a => Int -> Int -> Gen (Ratio a)
src/Common/View.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE GADTs #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -8,12 +9,13 @@ -- Stability : provisional -- Portability : portable (depends on ghc) ----- This module defines views on data-types+-- This module defines views on data-types, as described in "Canonical Forms+-- in Interactive Exercise Assistants" -- ----------------------------------------------------------------------------- module Common.View ( -- * Generalized monadic views- ViewM, match, build, makeView, biArr, identity, (>>>)+ ViewM, match, build, newView, makeView, biArr, identity, (>>>) , canonical, canonicalWith , Control.Arrow.Arrow(..), Control.Arrow.ArrowChoice(..) -- * Simple views@@ -21,13 +23,14 @@ , simplify, simplifyWith, viewEquivalent, viewEquivalentWith , isCanonical, isCanonicalWith, matchM, canonicalM, viewList -- * Some combinators- , listView, switchView, ( #> ), associativeView+ , swapView, listView, switchView, associativeView -- * Properties on views , propIdempotence, propSoundness, propNormalForm ) where -import Common.Traversable-import Control.Arrow hiding ((>>>))+import Common.Id+import Common.Classes+import Control.Arrow import Control.Monad import Data.Maybe import Test.QuickCheck@@ -36,81 +39,82 @@ ---------------------------------------------------------------------------------- -- Generalized monadic view --- For all v::View the following should hold:--- 1) simplify v a "is equivalent to" a--- 2) match (build b) equals Just b --- (but only for b that have at least one "a")------ Derived property: simplification is idempotent--data ViewM m a b = ViewM- { match :: a -> m b- , build :: b -> a- }--makeView :: Monad m => (a -> m b) -> (b -> a) -> ViewM m a b-makeView = ViewM--biArr :: Monad m => (a -> b) -> (b -> a) -> ViewM m a b-biArr f g = makeView (return . f) g--canonical :: Monad m => ViewM m a b -> a -> m a-canonical = canonicalWith id--canonicalWith :: Monad m => (b -> b) -> ViewM m a b -> a -> m a-canonicalWith f view = liftM (build view . f) . match view-------------------------------------------------------------------- Arrow combinators--identity :: Monad m => ViewM m a a -identity = makeView return id--(>>>) :: Monad m => ViewM m a b -> ViewM m b c -> ViewM m a c-v >>> w = makeView (\a -> match v a >>= match w) (build v . build w)+data ViewM m a b where+ Prim :: Id -> (a -> m b) -> (b -> a) -> ViewM m a b+ (:>>>:) :: ViewM m a b -> ViewM m b c -> ViewM m a c + First :: ViewM m a b -> ViewM m (a, c) (b, c)+ Second :: ViewM m b c -> ViewM m (a, b) (a, c)+ (:***:) :: ViewM m a c -> ViewM m b d -> ViewM m (a, b) (c, d)+ (:&&&:) :: ViewM m a b -> ViewM m a c -> ViewM m a (b, c)+ VLeft :: ViewM m a b -> ViewM m (Either a c) (Either b c)+ VRight :: ViewM m b c -> ViewM m (Either a b) (Either a c)+ (:+++:) :: ViewM m a c -> ViewM m b d -> ViewM m (Either a b) (Either c d)+ (:|||:) :: ViewM m a c -> ViewM m b c -> ViewM m (Either a b) c instance Monad m => C.Category (ViewM m) where id = identity- v . w = w >>> v- + v . w = w :>>>: v+ instance Monad m => Arrow (ViewM m) where- arr f = biArr f (error "Control.View.arr: function is not invertible")+ arr f = Prim (newId "views.arr") (return . f) (error "Control.View.arr: function is not invertible")+ first = First+ second = Second+ (***) = (:***:)+ (&&&) = (:&&&:) - first v = makeView - (\(a, c) -> match v a >>= \b -> return (b, c)) - (first (build v))+instance Monad m => ArrowChoice (ViewM m) where+ left = VLeft+ right = VRight+ (+++) = (:+++:)+ (|||) = (:|||:) - second v = makeView - (\(a, b) -> match v b >>= \c -> return (a, c)) - (second (build v))+----------------------------------------------------------------------------------+-- Operations on a view - v *** w = makeView - (\(a, c) -> liftM2 (,) (match v a) (match w c)) - (build v *** build w)+-- The preferred way of constructing a view+newView :: (IsId n, Monad m) => n -> (a -> m b) -> (b -> a) -> ViewM m a b+newView = Prim . newId - -- left-biased builder- v &&& w = makeView - (\a -> liftM2 (,) (match v a) (match w a)) - (\(b, _) -> build v b)+makeView :: Monad m => (a -> m b) -> (b -> a) -> ViewM m a b+makeView = newView "views.makeView" -instance Monad m => ArrowChoice (ViewM m) where- left v = makeView - (either (liftM Left . match v) (return . Right)) - (either (Left . build v) Right)+biArr :: Monad m => (a -> b) -> (b -> a) -> ViewM m a b+biArr f = makeView (return . f) - right v = makeView - (either (return . Left) (liftM Right . match v)) - (either Left (Right . build v))+match :: Monad m => ViewM m a b -> a -> m b+match view =+ case view of+ Prim _ f _ -> f+ v :>>>: w -> \a -> match v a >>= match w+ First v -> \(a, c) -> match v a >>= \b -> return (b, c)+ Second v -> \(a, b) -> match v b >>= \c -> return (a, c)+ v :***: w -> \(a, c) -> liftM2 (,) (match v a) (match w c)+ v :&&&: w -> \a -> liftM2 (,) (match v a) (match w a)+ VLeft v -> either (liftM Left . match v) (return . Right)+ VRight v -> either (return . Left) (liftM Right . match v)+ v :+++: w -> either (liftM Left . match v) (liftM Right . match w)+ v :|||: w -> either (match v) (match w) - v +++ w = makeView - (either (liftM Left . match v) (liftM Right . match w)) - (either (Left . build v) (Right . build w))+build :: ViewM m a b -> b -> a+build view = + case view of+ Prim _ _ f -> f+ v :>>>: w -> build v . build w+ First v -> first (build v)+ Second v -> second (build v)+ v :***: w -> build v *** build w+ v :&&&: _ -> build v . fst -- left-biased+ VLeft v -> either (Left . build v) Right+ VRight v -> either Left (Right . build v)+ v :+++: w -> either (Left . build v) (Right . build w)+ v :|||: _ -> Left . build v -- left-biased - -- left-biased builder- v ||| w = makeView - (either (match v) (match w))- (Left . build v)+canonical :: Monad m => ViewM m a b -> a -> m a+canonical = canonicalWith id +canonicalWith :: Monad m => (b -> b) -> ViewM m a b -> a -> m a+canonicalWith f view = liftM (build view . f) . match view+ --------------------------------------------------------------- -- Simple views (based on a particular monad) @@ -150,23 +154,27 @@ canonicalM :: Monad m => View a b -> a -> m a canonicalM v = maybe (Prelude.fail "no match") return . canonicalWith id v -viewList :: Crush m => ViewM m a b -> ViewList a b+viewList :: (Crush m, Monad m) => ViewM m a b -> ViewList a b viewList v = makeView (crush . match v) (build v) --------------------------------------------------------------- -- Some combinators +identity :: Monad m => ViewM m a a +identity = newView "views.identity" return id++swapView :: View (a, b) (b, a)+swapView = + let swap (a, b) = (b, a)+ in newView "views.swap" (return . swap) swap+ listView :: Monad m => ViewM m a b -> ViewM m [a] [b] listView v = makeView (mapM (match v)) (map (build v)) switchView :: (Monad m, Switch f) => ViewM m a b -> ViewM m (f a) (f b) switchView v = makeView (switch . fmap (match v)) (fmap (build v))--( #> ) :: MonadPlus m => (a -> Bool) -> ViewM m a b -> ViewM m a b-p #> v = makeView f (build v)- where f a = guard (p a) >> match v a -associativeView :: View a (a,a) -> ViewList a (a,a)+associativeView :: View a (a, a) -> ViewList a (a, a) associativeView v = makeView (reverse . f) (build v) where f a = case matchM v a of@@ -189,3 +197,21 @@ propNormalForm :: (Show a, Eq a) => Gen a -> View a b -> Property propNormalForm g v = forAll g $ \a -> a == simplify v a++{- proving a parameterized view equivalent to one with a "context"++abstr1 :: (a -> View b c) -> View (a, b) (a, c)+abstr1 fun = makeView f g+ where+ f (a, b) = do+ c <- match (fun a) b+ return (a, c)+ g (a, c) = (a, build (fun a) c)+ +abstr2 :: View (a, b) (a, c) -> (a -> View b c)+abstr2 v a = makeView f g+ where+ f b = do + (_, c) <- match v (a, b)+ return c+ g c = snd (build v (a, c)) -}
src/Documentation/DefaultPage.hs view
@@ -11,17 +11,14 @@ ----------------------------------------------------------------------------- module Documentation.DefaultPage where -import Common.Context import Common.Exercise-import Common.Transformation import Control.Monad import Service.DomainReasoner-import Service.ServiceList+import Service.Types import System.Directory import System.FilePath import Text.HTML import qualified Text.XML as XML-import Data.Char generatePage :: String -> String -> HTMLBuilder -> DomainReasoner () generatePage = generatePageAt 0@@ -45,19 +42,18 @@ footer version header :: Int -> HTMLBuilder-header level = center $ do- let f m = text "[" >> space >> m >> space >> text "]"- f $ link (up level ++ exerciseOverviewPageFile) $ text "Exercises"- replicateM_ 5 space- f $ link (up level ++ "services.html") $ text "Services"- replicateM_ 5 space- f $ link (up level ++ "tests.html") $ text "Tests"- replicateM_ 5 space- f $ link (up level ++ "coverage/hpc_index.html") $ text "Coverage"- replicateM_ 5 space- f $ link (up level ++ "api/index.html") $ text "API"+header level = do + divClass "menu" $ do+ make exerciseOverviewPageFile "Exercises"+ make "services.html" "Services"+ make "tests.html" "Tests"+ make "coverage/hpc_index.html" "Coverage"+ make "api/index.html" "API" hr-+ where+ make target s = f $ link (up level ++ target) $ text s+ f m = spaces 3 >> text "[" >> space >> m >> space >> text "]" >> spaces 3+ footer :: String -> HTMLBuilder footer version = do hr @@ -69,62 +65,33 @@ findTitle :: HTMLBuilder -> String findTitle = maybe "" XML.getData . XML.findChild "h1" . XML.makeXML "page" +filePathId :: HasId a => a -> FilePath+filePathId a = foldr (\x y -> x ++ "/" ++ y) (unqualified a) (qualifiers a)+ ------------------------------------------------------------ -- Paths and files -ruleImagePath :: Exercise a -> String-ruleImagePath ex = "exercises/" ++ f (domain (exerciseCode ex)) ++ "/" ++ f (description ex) ++ "/"- where f = filter isAlphaNum . map toLower--exercisePagePath :: ExerciseCode -> String-exercisePagePath code = "exercises/" ++ domain code ++ "/"--servicePagePath :: String-servicePagePath = "services/" --ruleImageFile :: Exercise a -> Rule (Context a) -> String-ruleImageFile ex r = ruleImagePath ex ++ "rule" ++ name r ++ ".png"--ruleImageFileHere :: Exercise a -> Rule (Context a) -> String-ruleImageFileHere ex r = - filter (not . isSpace) (identifier (exerciseCode ex)) - ++ "/rule" ++ filter isAlphaNum (name r) ++ ".png"--exerciseOverviewPageFile :: String-exerciseOverviewPageFile = "exercises.html"+exerciseOverviewPageFile, exerciseOverviewAllPageFile, + serviceOverviewPageFile, testsPageFile :: String -exerciseOverviewAllPageFile :: String+exerciseOverviewPageFile = "exercises.html" exerciseOverviewAllPageFile = "exercises-all.html"--serviceOverviewPageFile :: String-serviceOverviewPageFile = "services.html"--exercisePageFile :: ExerciseCode -> String-exercisePageFile code = - exercisePagePath code - ++ filter (not . isSpace) (identifier code) - ++ ".html"--exerciseStrategyFile :: ExerciseCode -> String-exerciseStrategyFile code = - exercisePagePath code- ++ filter (not . isSpace) (identifier code)- ++ "-strategy.html"--exerciseRulesFile :: ExerciseCode -> String-exerciseRulesFile code = - exercisePagePath code- ++ filter (not . isSpace) (identifier code)- ++ "-rules.html"+serviceOverviewPageFile = "services.html"+testsPageFile = "tests.html" -exerciseDerivationsFile :: ExerciseCode -> String-exerciseDerivationsFile code = - exercisePagePath code- ++ filter (not . isSpace) (identifier code)- ++ "-derivations.html"+exercisePageFile, exerciseDerivationsFile, exerciseStrategyFile,+ exerciseDiagnosisFile, ruleFile :: HasId a => a -> FilePath+exercisePageFile a = filePathId a ++ ".html"+exerciseDerivationsFile a = filePathId a ++ "-derivations.html"+exerciseStrategyFile a = filePathId a ++ "-strategy.html"+exerciseDiagnosisFile a = filePathId a ++ "-diagnosis.html"+ruleFile a = filePathId ("rule" # getId a) ++ ".html" servicePageFile :: Service -> String-servicePageFile srv = servicePagePath ++ serviceName srv ++ ".html"+servicePageFile srv = "services/" ++ filePathId srv ++ ".html"++diagnosisExampleFile :: Id -> String+diagnosisExampleFile a = "examples/" ++ showId a ++ ".txt" ------------------------------------------------------------ -- Utility functions
+ src/Documentation/DerivationUnitTests.hs view
@@ -0,0 +1,39 @@+----------------------------------------------------------------------------- +-- 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 Documentation.DerivationUnitTests (main) where + +import Control.Monad +import Common.Exercise +import Text.XML +import Domain.Math.Power.Exercises + +base = "test/dwo-derivations" + +main = make powerOfAExercise + +make :: Exercise a -> IO () +make ex = zipWithM_ (makeTest ex) [1 ..] (examples ex) + +makeTest :: Exercise a -> Int -> a -> IO () +makeTest ex n a = do + let file = base ++ "/" ++ show (exerciseCode ex) ++ show n ++ ".xml" + putStrLn $ "Writing " ++ file + writeFile file $ showXML $ makeRequest a ex + +makeRequest :: a -> Exercise a -> XML +makeRequest a ex = makeXML "request" $ do + "service" .=. "derivation" + "exerciseid" .=. show (exerciseCode ex) + "encoding" .=. "string" + element "state" $ + element "expr" $ + text $ prettyPrinter ex a
src/Documentation/ExercisePage.hs view
@@ -11,73 +11,89 @@ ----------------------------------------------------------------------------- module Documentation.ExercisePage (makeExercisePage) where +import Common.Context import Common.Exercise+import Common.Derivation import Common.Strategy hiding (not, replicate) import Common.Transformation-import Service.ExercisePackage-import Service.StrategyInfo-import Service.DomainReasoner-import Service.TypedAbstractService hiding (exercise)+import Common.Utils (Some(..), splitAtSequence) import Control.Monad+import Data.Char import Data.List-import Common.Utils (commaList, Some(..)) import Data.Maybe+import Documentation.DefaultPage+import Documentation.RulePresenter+import Service.BasicServices+import Service.Diagnose+import Service.DomainReasoner+import Service.ExercisePackage+import Service.State+import Service.StrategyInfo+import System.Directory import System.Random-import qualified Data.Map as M-import Service.RulesInfo (rewriteRuleToFMP, collectExamples) import Text.HTML-import Text.OpenMath.Object-import Text.OpenMath.FMP-import qualified Text.XML as XML-import Documentation.DefaultPage makeExercisePage :: String -> ExercisePackage a -> DomainReasoner () makeExercisePage dir pkg = do- let ex = exercise pkg- make = generatePageAt 2 dir . ($ (exerciseCode ex))- make exercisePageFile (exercisePage pkg)+ let ex = exercise pkg+ make = makeId pkg+ makeId a = generatePageAt (length (qualifiers a)) dir . ($ (getId a))+ exFile = dir ++ "/" ++ diagnosisExampleFile (getId ex)++ exampleFileExists <- liftIO (doesFileExist exFile)++ make exercisePageFile (exercisePage exampleFileExists pkg) make exerciseStrategyFile (strategyPage ex)- make exerciseRulesFile (rulesPage ex) unless (null (examples (exercise pkg))) $ make exerciseDerivationsFile (derivationsPage ex)+ when (exampleFileExists) $ do+ xs <- liftIO (readFile exFile)+ make exerciseDiagnosisFile (diagnosisPage xs pkg)+ `catchError` \_ -> return () -exercisePage :: ExercisePackage a -> HTMLBuilder-exercisePage pkg = do- h1 (description ex)+exercisePage :: Bool -> ExercisePackage a -> HTMLBuilder+exercisePage exampleFileExists pkg = do+ idboxHTML "strategy" (getId pkg) h2 "1. General information"- table - [ [bold $ text "Code", ttText (show $ exerciseCode ex)]- , [bold $ text "Status", text (show $ status ex)]- , [ bold $ text "OpenMath support"++ let bolds (x:xs) = bold x:xs+ bolds [] = []++ table $ map bolds+ [ [ text "Code", ttText (showId ex)]+ , [ text "Status", text (show $ status ex)]+ , [ text "Strategy"+ , link (up level ++ exerciseStrategyFile exid) $+ text (showId $ strategy ex)+ ]+ , [ text "OpenMath support" , text $ showBool $ withOpenMath pkg ]- , [ bold $ text "Textual feedback"+ , [ text "Textual feedback" , text $ showBool $ isJust $ getExerciseText pkg ]- , [ bold $ text "Restartable strategy"+ , [ text "Restartable strategy" , text $ showBool $ canBeRestarted ex ] - , [ bold $ text "Exercise generator"+ , [ text "Exercise generator" , text $ showBool $ isJust $ randomExercise ex ]- , [ bold $ text "Examples"+ , [ text "Examples" , text $ show $ length $ examples ex ] ]- - para $ link (up 2 ++ exerciseStrategyFile code) $- text "See strategy details" h2 "2. Rules"- let rs = rulesInStrategy (strategy ex)- f r = [ text (name r)+ let rs = rulesInStrategy (strategy ex)+ ups = up (length (qualifiers pkg)) + f r = [ link (ups ++ ruleFile r) $ ttText (showId r) , text $ showBool $ isBuggyRule r , text $ showBool $ hasArguments r , text $ showBool $ r `elem` rs- , text $ concat $ intersperse "," (ruleGroups r)+ , text $ concat $ intersperse "," $ map showId $ ruleGroups r , when (isRewriteRule r) $- image (ruleImageFileHere ex r)+ ruleToHTML (Some ex) r ] table ( [bold $ text "Rule name", bold $ text "Buggy" , bold $ text "Args" @@ -86,95 +102,107 @@ ] : map f (ruleset ex) )- para $ link (up 2 ++ exerciseRulesFile code) $- text "See rule details"- - + when exampleFileExists $ do+ para $ link (up level ++ exerciseDiagnosisFile exid) $ do+ br+ text "See diagnosis examples"+ h2 "3. Example"- let state = generateWith (mkStdGen 0) ex 5- preText (showDerivation ex (term state))- unless (null (examples ex)) $ - link (up 2 ++ exerciseDerivationsFile code) (text "More examples")+ let state = generateWith (mkStdGen 0) pkg 5+ derivationHTML ex (stateTerm state)+ para $ unless (null (examples ex)) $ + link (up level ++ exerciseDerivationsFile exid) (text "More examples") where- ex = exercise pkg- code = exerciseCode ex+ ex = exercise pkg+ exid = getId ex+ level = length (qualifiers pkg) strategyPage :: Exercise a -> HTMLBuilder strategyPage ex = do h1 title h2 "1. Representation in XML"- preText (XML.showXML (strategyToXML (strategy ex)))+ highlightXML True (strategyToXML (strategy ex)) h2 "2. Locations" - let f (loc, e) = [text (show loc), indent (locationDepth loc) >> g e]- g (Left a) = text (strategyName a)- g (Right a) = text (name a ++ " (rule)") - indent n = text (replicate (3*n) '.')+ let f (loc, a) = + [text (show loc), indent (length loc) >> text (showId a)]+ indent n = text (replicate (3*n) '.') table ( [bold $ text "Location", bold $ text "Label"] : map f (strategyLocations (strategy ex)) ) where- code = exerciseCode ex- title = "Strategy for " ++ show code--rulesPage :: Exercise a -> HTMLBuilder-rulesPage ex = do- h1 title- -- Groups- let groups = sort (nub (concatMap ruleGroups (ruleset ex)))- unless (null groups) $ do- ul $ flip map groups $ \g -> do- bold $ text $ g ++ ":"- space- let elems = filter ((g `elem`) . ruleGroups) (ruleset ex)- text $ commaList $ map name elems- - -- General info- forM_ (zip [1..] (ruleset ex)) $ \(i, r) -> do- h2 (show i ++ ". " ++ show r)- para $ text (ruleDescription r)- para $ table - [ [bold $ text "Buggy", text $ showBool (isBuggyRule r)]- , [bold $ text "Rewrite rule", text $ showBool (isRewriteRule r)]- , [bold $ text "Groups", text $ commaList $ ruleGroups r]- , [bold $ text "Siblings", text $ commaList $ ruleSiblings r] - ]- when (isRewriteRule r) $ para $- image (ruleImageFileHere ex r)- -- Examples- let ys = M.findWithDefault [] (name r) exampleMap- unless (null ys) $ do- h3 "Examples"- forM_ (take 3 ys) $ \(a, b) -> para $ tt $ - preText $ prettyPrinter ex a ++ "\n =>\n" ++ prettyPrinter ex b- - -- FMPS- let xs = getRewriteRules r- unless (null xs) $ do- h3 "Formal Mathematical Properties"- forM_ xs $ \(Some rr, b) -> para $ do- let fmp = rewriteRuleToFMP b rr- ttText $ show $ XML.makeXML "FMP" $ - XML.builder (omobj2xml (toObject fmp))- where- code = exerciseCode ex- title = "Strategy for " ++ show code- exampleMap = collectExamples ex+ title = "Strategy for " ++ showId ex derivationsPage :: Exercise a -> HTMLBuilder derivationsPage ex = do- unless (errs==0) $ - errorLine $ preText $ "Warning: " ++ show errs ++ " example(s) with an incorrect derivation" h1 "Examples"- forM_ (zip [1 ..] ds) $ \(i, d) -> do+ forM_ (zip [1::Int ..] (examples ex)) $ \(i, a) -> do h2 (show i ++ ".")- preText d+ derivationHTML ex a++derivationHTML :: Exercise a -> a -> HTMLBuilder+derivationHTML ex a = divClass "derivation" $ do + pre $ derivationM (forStep ups) (forTerm ex) der+ unless (ok der) $+ divClass "error" $ text "<<not ready>>" where- ds = map (showDerivation ex) (examples ex)- errs = let p s = "<<no derivation>>" `isSuffixOf` s - || "<<not ready>>" `isSuffixOf` s- in length $ filter p ds- -errorLine :: HTMLBuilder -> HTMLBuilder-errorLine b = XML.element "font" $ do- "color" XML..=. "red"- bold b+ ups = length (qualifiers ex)+ der = derivationDiffEnv (defaultDerivation ex a)+ ok = maybe False (isReady ex) . fromContext . last . terms++idboxHTML :: String -> Id -> HTMLBuilder+idboxHTML kind i = divClass "idbox" $ do+ font "id" $ ttText (showId i)+ spaces 3+ text $ "(" ++ kind ++ ")"+ unless (null $ description i) $ do+ br+ italic (text (description i))++diagnosisPage :: String -> ExercisePackage a -> HTMLBuilder+diagnosisPage xs pkg = do+ h1 ("Diagnosis examples for " ++ showId pkg)+ forM_ (zip [1::Int ..] (mapMaybe f (lines xs))) $ \(i, (t0, t1, expl)) -> do + h2 (show i ++ ".")+ preText (t0 ++ "\n =>\n" ++ t1)+ para $ do+ unless (null expl) $ do + bold $ text "Description:"+ space+ text expl+ br+ bold $ text "Diagnosis:"+ space+ text (getDiagnosis t0 t1)+ where+ ex = exercise pkg+ f a = do + (x, b) <- splitAtSequence "==>" a+ let (y, z) = fromMaybe (b, "") (splitAtSequence ":::" b)+ trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace+ return (trim x, trim y, trim z)+ + getDiagnosis t0 t1 = + case (parser ex t0, parser ex t1) of+ (Left msg, _) -> "parse error (before): " ++ msg+ (_, Left msg) -> "parse error (afterr): " ++ msg+ (Right a, Right b) -> show (diagnose (emptyState pkg a) b)+ +forStep :: HasId a => Int -> (a, Environment) -> HTMLBuilder +forStep n (i, env) = do + spaces 3+ text "=>"+ space+ let target = up n ++ ruleFile i+ make | null (description i) = link target+ | otherwise = linkTitle target (description i)+ make (text (unqualified i))+ br+ unless (nullEnv env) $ do+ spaces 6+ text (show env)+ br++forTerm :: Exercise a -> Context a -> HTMLBuilder+forTerm ex ca = do+ text (prettyPrinterContext ex ca)+ br
− src/Documentation/LatexRules.hs
@@ -1,140 +0,0 @@------------------------------------------------------------------------------ --- Copyright 2010, 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 Documentation.LatexRules (makeLatexRules) where - -import Common.Exercise -import Common.Rewriting -import Common.Transformation -import Common.Utils -import Control.Monad -import Data.Char -import Data.List -import Data.Maybe -import System.Directory -import System.Time - -makeLatexRules :: String -> Exercise a -> IO () -makeLatexRules dir ex = do - let code = exerciseCode ex - path = dir ++ "/" ++ domain code ++ "/" ++ filter (/= ' ') (identifier code) - -- Exercise document - let rules = concatMap getRewriteRules (ruleset ex) - unless (null rules) $ do - createDirectoryIfMissing True path - doc <- makeDocument ex - let filename = path ++ "/overview.lhs" - putStrLn $ "Creating " ++ filename - writeFile filename doc - -- individual rules - forM_ (ruleset ex) $ \r -> - case makeSingleRule (domain code ++ "/" ++ domain code ++ ".fmt") r of - Nothing -> return () - Just txt -> do - let filename = path ++ "/rule" ++ filter isAlphaNum (name r) ++ ".lhs" - putStrLn $ "Creating " ++ filename - writeFile filename txt - -{- -exerciseRulesToTeX :: Exercise a -> String -exerciseRulesToTeX ex = unlines . map ruleToTeX . concatMap getRewriteRules . ruleset $ ex --} - -ruleToTeX :: (Some RewriteRule, Bool) -> Maybe String -ruleToTeX (Some r, sound) = do - txt <- showRewriteRule sound r - return $ "RewriteRule " ++ withoutDigits (ruleName r) - ++ " (" ++ txt ++ ")" - - ------------------------------------------------------- - -makeSingleRule :: String -> Rule a -> Maybe String -makeSingleRule dom r - | null (getRewriteRules r) = Nothing - | otherwise = Just $ texHeader (Just dom) ++ texBody Nothing content - where - content = unlines $ - [ "\\pagestyle{empty}" - , formatRuleName (name r) - , "\\begin{code}" - ] ++ - map (filter (/= '"') . fromMaybe "" . ruleToTeX) (getRewriteRules r) ++ - [ "\\end{code}" - ] - - -makeDocument :: Exercise a -> IO String -makeDocument ex = do - let code = exerciseCode ex - time <- getClockTime - return $ - texHeader (Just $ domain code ++ "/" ++ domain code ++ ".fmt") ++ - texBody (Just $ show time) (texSectionRules ex) - ------------------------------------------------------- - -texHeader :: Maybe String -> String -texHeader fmt = unlines - [ "\\documentclass{article}" - , "" - , "%include lhs2TeX.fmt" - , "%format RewriteRule (a) (b) = \"\\rewriterule{\"a\"}{\"b\"}\"" - , "%format ~> = \"\\:\\leadsto\\:\"" - , "%format /~> = \"\\:\\not\\leadsto\\:\"" - , maybe "" ("%include "++) fmt - , "" - , "\\newcommand{\\rewriterule}[2]{#1:\\quad #2}" - , "\\newcommand{\\rulename}[1]{\\mbox{\\sc #1}}" - ] - -texBody :: Maybe String -> String -> String -texBody date content = unlines - [ "\\begin{document}" - , content - , maybe "" (\s -> "\\par\\vspace*{5mm}\\noindent\\footnotesize{@(generated on " ++ s ++ ")@}") date - , "\\end{document}" - ] - -texSectionRules :: Exercise a -> String -texSectionRules ex = unlines - [ "\\section{Rewrite rules}" - , formats - , makeGroup Nothing - , unlines $ map (makeGroup . Just) groups - ] - where - rules = concatMap getRewriteRules (ruleset ex) - groups = nub (concatMap ruleGroups (ruleset ex)) - names = let f (Some r, _) = ruleName r - in nub (map f rules) - formats = unlines (map formatRuleName names) - - makeGroup :: Maybe String -> String - makeGroup mgroup = unlines - [ maybe "" (\s -> "\\subsection{" ++ s ++ "}") mgroup - , "\\begin{code}" - , unlines $ map (filter (/= '"')) xs - , "\\end{code}" - ] - where - p x = maybe (null $ ruleGroups x) (`elem` ruleGroups x) mgroup - xs = mapMaybe ruleToTeX $ concatMap getRewriteRules $ filter p $ ruleset ex - -formatRuleName :: String -> String -formatRuleName s = "%format " ++ withoutDigits s ++ " = \"\\rulename{" ++ s ++ "}\"" - -withoutDigits :: String -> String -withoutDigits = concatMap f - where - f c | isAlpha c = [c] - | isDigit c = "QX" ++ [chr (ord c + 49)] - | otherwise = []
src/Documentation/Make.hs view
@@ -11,27 +11,51 @@ ----------------------------------------------------------------------------- module Documentation.Make (DocItem(..), makeDocumentation) where +import Common.TestSuite import Common.Utils (Some(..)) +import Control.Monad +import Data.Maybe import Service.DomainReasoner import Documentation.SelfCheck -import Documentation.LatexRules import Documentation.ExercisePage +import Documentation.RulePage +import Documentation.TestsPage import Documentation.ServicePage import Documentation.OverviewPages -data DocItem = Pages String | LatexRules String | SelfCheck String +data DocItem = Pages | SelfCheck | BlackBox (Maybe String) deriving Eq -makeDocumentation :: DocItem -> DomainReasoner () -makeDocumentation doc = - case doc of - Pages dir -> do - makeOverviewExercises dir - makeOverviewServices dir - getPackages >>= mapM_ (\(Some pkg) -> makeExercisePage dir pkg) - getServices >>= mapM_ (\s -> makeServicePage dir s) - SelfCheck dir -> - performSelfCheck dir - LatexRules dir -> - let f (Some ex) = makeLatexRules dir ex - in getExercises >>= liftIO . mapM_ f+makeDocumentation :: String -> String -> DocItem -> DomainReasoner () +makeDocumentation docDir testDir item = + case item of + Pages -> do + report "Generating overview pages" + makeOverviewExercises docDir + makeOverviewServices docDir + report "Generating exercise pages" + pkgs <- getPackages + forM_ pkgs $ \(Some pkg) -> + makeExercisePage docDir pkg + report "Generating rule pages" + makeRulePages docDir + report "Generating service pages" + getServices >>= mapM_ (makeServicePage docDir) + report "Running tests" + makeTestsPage docDir testDir + SelfCheck -> do + checks <- selfCheck testDir + result <- liftIO (runTestSuiteResult checks) + liftIO (printSummary result) + BlackBox mdir -> do + run <- runWithCurrent + checks <- liftIO $ blackBoxTests run (fromMaybe testDir mdir) + result <- liftIO $ runTestSuiteResult checks + liftIO (printSummary result) + +report :: String -> DomainReasoner () +report s = liftIO $ do + let line = replicate 75 '-' + putStrLn line + putStrLn ("--- " ++ s) + putStrLn line
src/Documentation/OverviewPages.hs view
@@ -16,11 +16,12 @@ import Documentation.DefaultPage import Data.Char import Data.List+import Data.Maybe import Control.Monad-import Common.Utils (Some(..))+import Common.Utils (Some(..), safeHead) import Common.Exercise-import Service.ServiceList import Service.DomainReasoner+import Service.Types import Text.HTML makeOverviewExercises :: String -> DomainReasoner ()@@ -47,40 +48,40 @@ text "all exercises" text ", including the ones under development" - forM_ (zip [1..] groupedList) $ \(i, (dom, xs)) -> do+ forM_ (zip [1::Int ..] (grouping list)) $ \(i, (dom, xs)) -> do h2 (show i ++ ". " ++ dom)- noBorderTable (map makeRow xs) + table (map makeRow xs) where title | showAll = "All exercises" | otherwise = "Exercises" makeRow (Some ex) = - [ do tt bullet >> space- link (exercisePageFile code) $ ttText (show code)+ [ link (exercisePageFile code) $ ttText (show code) , do spaces 10 f (status ex) spaces 10 , text $ description ex ] where- code = exerciseCode ex+ code = getId ex f st = italic $ text ("(" ++ map toLower (show st) ++ ")") - groupedList = process list+ grouping = map g . groupBy eq . sortBy cmp . filter p where- process = map g . groupBy eq . sortBy cmp . filter p- - cmp (Some a) (Some b) = exerciseCode a `compare` exerciseCode b+ cmp (Some a) (Some b) = compareId (exerciseId a) (exerciseId b) eq a b = f a == f b- f (Some ex) = domain (exerciseCode ex)- g xs = (f (head xs), xs)+ f (Some ex) = safeHead (qualifiers (exerciseId ex))+ g xs = (fromMaybe "" (f (head xs)), xs) p (Some ex) = showAll || isPublic ex serviceOverviewPage :: [Service] -> HTMLBuilder serviceOverviewPage list = do h1 "Services"- let sorted = sortBy (\x y -> serviceName x `compare` serviceName y) list- ul $ flip map sorted $ \s -> do- link (servicePageFile s) (ttText (serviceName s))- when (serviceDeprecated s) $- space >> text "(deprecated)"+ let (xs, ys) = partition serviceDeprecated (sortBy compareId list)+ make s = link (servicePageFile s) (ttText (showId s))+ ul $ map make ys+ unless (null xs) $ do+ h2 "Deprecated"+ ul $ map make xs++
+ src/Documentation/RulePage.hs view
@@ -0,0 +1,119 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Documentation.RulePage (makeRulePages) where++import Common.Context+import Common.Exercise+import Common.Transformation+import Common.Utils (commaList, Some(..))+import Control.Monad+import Data.List+import Documentation.DefaultPage+import Documentation.RulePresenter+import Service.DomainReasoner+import Service.ExercisePackage+import Service.RulesInfo (rewriteRuleToFMP, collectExamples, ExampleMap)+import Text.HTML+import Text.OpenMath.FMP+import Text.OpenMath.Object+import qualified Data.Map as M+import qualified Text.XML as XML++data ExItem a = EI (ExercisePackage a) (ExampleMap a)++makeRulePages :: String -> DomainReasoner ()+makeRulePages dir = do+ pkgs <- getPackages + let exMap = M.fromList + [ (getId pkg, Some (EI pkg (collectExamples (exercise pkg))))+ | Some pkg <- pkgs+ ]+ ruleMap = M.fromListWith (++)+ [ (getId r, [Some pkg]) + | Some pkg <- pkgs+ , r <- ruleset (exercise pkg) + ]+ forM_ (M.toList ruleMap) $ \(ruleId, list@(Some pkg:_)) -> do+ let noExamples = Some (EI pkg M.empty) + level = length (qualifiers ruleId) + 1+ usedIn = sortBy compareId [ getId pkg1 | Some pkg1 <- list ]+ case M.findWithDefault noExamples (getId pkg) exMap of+ Some (EI pkg1 e) -> do+ let ex = exercise pkg1+ forM_ (getRule ex ruleId) $ \r ->+ generatePageAt level dir (ruleFile ruleId) $+ rulePage ex e usedIn r++rulePage :: Exercise a -> ExampleMap a -> [Id] -> Rule (Context a) -> HTMLBuilder+rulePage ex exMap usedIn r = do+ idboxHTML "rule" (getId r)+ let idList = text . commaList . map showId+ para $ table + [ [bold $ text "Buggy", text $ showBool (isBuggyRule r)]+ , [bold $ text "Rewrite rule", text $ showBool (isRewriteRule r)]+ , [bold $ text "Groups", idList $ ruleGroups r]+ , [bold $ text "Siblings", idList $ ruleSiblings r] + ]+ when (isRewriteRule r) $ para $+ ruleToHTML (Some ex) r++ h3 "Used in exercises"+ let f a = link (up ups ++ exercisePageFile a) (tt $ text $ show a)+ ups = length (qualifiers r) + 1+ ul $ map f usedIn++ -- Examples+ let ys = M.findWithDefault [] (getId r) exMap+ unless (null ys) $ do+ h3 "Examples"+ forM_ (take 3 ys) $ \(a, b) -> para $ divClass "step" $ pre $ do + forTerm ex (inContext ex a)+ forStep ups (getId r, emptyEnv)+ forTerm ex (inContext ex b)+ + -- FMPS+ let xs = getRewriteRules r+ unless (null xs) $ do+ h3 "Formal Mathematical Properties"+ forM_ xs $ \(Some rr, b) -> para $ do+ let fmp = rewriteRuleToFMP b rr+ highlightXML False $ XML.makeXML "FMP" $ + XML.builder (omobj2xml (toObject fmp))++idboxHTML :: String -> Id -> HTMLBuilder+idboxHTML kind i = divClass "idbox" $ do+ para $ do + font "id" $ ttText (showId i)+ spaces 3+ text $ "(" ++ kind ++ ")"+ unless (null $ description i) $+ para $ italic $ text (description i)++forStep :: Int -> (Id, Environment) -> HTMLBuilder +forStep n (i, env) = do + spaces 3+ text "=>"+ space+ let target = up n ++ ruleFile i+ make | null (description i) = link target+ | otherwise = linkTitle target (description i)+ make (text (unqualified i))+ br+ unless (nullEnv env) $ do+ spaces 6+ text (show env)+ br++forTerm :: Exercise a -> Context a -> HTMLBuilder+forTerm ex ca = do+ text (prettyPrinterContext ex ca)+ br
+ src/Documentation/RulePresenter.hs view
@@ -0,0 +1,119 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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 Documentation.RulePresenter (ruleToHTML) where + +import Common.Library +import Control.Monad +import Common.Utils (Some(..), safeHead) +import Common.Rewriting.Term +import Data.List +import Text.HTML + +ruleToHTML :: Some Exercise -> Rule a -> HTMLBuilder +ruleToHTML ex r = + forM_ (getRewriteRules r) $ \(Some rr, b) -> + rewriteRuleToHTML b ex rr + +rewriteRuleToHTML :: Bool -> Some Exercise -> RewriteRule a -> HTMLBuilder +rewriteRuleToHTML sound ex r = do + let lhs :~> rhs = ruleSpecTerm r + -- showRuleName (unqualified r) + -- spaces 3 + showTerm ex lhs + spaces 3 + showLeadsTo sound + spaces 3 + showTerm ex rhs + br + +{- +showRuleName :: String -> HTMLBuilder +showRuleName s = text ("[" ++ s ++ "]") +-} + +showLeadsTo :: Bool -> HTMLBuilder +showLeadsTo sound = text (if sound then "\x21D2" else "\x21CF") + +showTerm :: Some Exercise -> Term -> HTMLBuilder +showTerm (Some ex) = text . rec + where + rec term = + case term of + Var s -> s + Num i -> show i + Float a -> show a + Meta n -> showMeta ex n + _ -> concatMap (either id recp) $ + case getSpine term of + (Con s, xs) -> + case specialSymbol s xs of + Just ys -> ys + Nothing -> spaced (Left (show s) : map Right xs) + (x, xs) -> spaced (map Right (x:xs)) + + recp term = parIf (isApply term) (rec term) + spaced = intersperse (Left " ") + + isApply (Apply _ _) = True + isApply _ = False + + parIf b s = if b then "(" ++ s ++ ")" else s + +specialSymbol :: Symbol -> [Term] -> Maybe [Either String Term] +-- constants +specialSymbol s [] + | sameSymbol s "logic1.true" = con "T" + | sameSymbol s "logic1.false" = con "F" + | sameSymbol s "relalg.universe" = con "V" -- universe + | sameSymbol s "relalg.ident" = con "I" -- identity + where + con x = return [Left x] +-- unary symbols +specialSymbol s [a] + | sameSymbol s "logic1.not" = pref "\172" -- "~" + | sameSymbol s "arith1.unary_minus" = pref "-" + | sameSymbol s "relalg.not" = post "\x203E" + | sameSymbol s "relalg.inv" = post "~" + where + pref x = return [Left x, Right a] + post x = return [Right a, Left x] +-- binary symbols +specialSymbol s [a, b] + | sameSymbol s "logic1.or" = bin " \8744 " -- "||" + | sameSymbol s "logic1.and" = bin " \8743 " -- "&&" + | sameSymbol s "logic1.implies" = bin " \8594 " -- "->" + | sameSymbol s "logic1.equivalent" = bin " \8596 " -- "<->" + | sameSymbol s "relation1.eq" = bin " = " + | sameSymbol s "arith1.plus" = bin "+" + | sameSymbol s "arith1.minus" = bin "-" + | sameSymbol s "arith1.power" = bin "^" + | sameSymbol s "arith1.times" = bin "\x00B7" -- "*" + | sameSymbol s "arith1.divide" = bin "/" + | sameSymbol s "relalg.conj" = bin " \x2229 " -- intersect + | sameSymbol s "relalg.disj" = bin " \x222A " -- union + | sameSymbol s "relalg.comp" = bin " ; " -- composition + | sameSymbol s "relalg.add" = bin " \x2020 " -- relative addition/dagger + where + bin x = return [Right a, Left x, Right b] +specialSymbol s1 [Apply (Apply (Con s2) x) a] + | sameSymbol s1 "calculus1.diff" && sameSymbol s2 "fns1.lambda" = + return [Left "D(", Right x, Left ") ", Right a] +specialSymbol _ _ = Nothing + +sameSymbol :: Symbol -> String -> Bool +sameSymbol = (==) . show + +showMeta :: Exercise a -> Int -> String +showMeta ex n + | safeHead (qualifiers ex) == Just "logic" = [ [c] | c <- ['p'..] ] !! n + | safeHead (qualifiers ex) == Just "relationalgebra" = [ [c] | c <- ['r'..] ] !! n + | otherwise = [ [c] | c <- ['a'..] ] !! n
src/Documentation/SelfCheck.hs view
@@ -9,87 +9,87 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Documentation.SelfCheck (performSelfCheck) where+module Documentation.SelfCheck (selfCheck, blackBoxTests) where -import Control.Monad.Trans import System.Directory-import Common.Utils (reportTest, useFixedStdGen, Some(..), snd3)+import Common.TestSuite+import Common.Utils (useFixedStdGen, Some(..), snd3) import Common.Exercise import Service.ExercisePackage-import qualified Common.Strategy.Grammar as Grammar import Control.Monad import Service.Request import Service.DomainReasoner--import qualified Domain.LinearAlgebra.Checks as LA import Service.ModeJSON import Service.ModeXML--import qualified Domain.Math.Numeric.Tests as MathNum-import qualified Domain.Math.Polynomial.Tests as MathPoly-import qualified Domain.Math.SquareRoot.Tests as MathSqrt-import qualified Domain.Math.Data.Interval as MathInterval-+import qualified Text.OpenMath.Tests as OpenMath import qualified Text.UTF8 as UTF8 import qualified Text.JSON as JSON import Data.List-import System.Time -performSelfCheck :: String -> DomainReasoner ()-performSelfCheck dir = totalDiff $ do- timeDiff $ liftIO $ do- putStrLn "* 1. Domain checks"- Grammar.checks- MathNum.main- MathPoly.tests- MathSqrt.tests- MathInterval.testMe- LA.checks- UTF8.testEncoding- JSON.testMe-- liftIO $ putStrLn "* 2. Exercise checks"- pkgs <- getPackages- forM_ pkgs $ \(Some pkg) ->- timeDiff $ liftIO $ checkExercise (exercise pkg)-- timeDiff $ do- liftIO $ putStrLn "* 3. Unit tests"- n <- unitTests dir- liftIO $ putStrLn $ "** Number of unit tests: " ++ show n+selfCheck :: String -> DomainReasoner TestSuite+selfCheck dir = do+ pkgs <- getPackages+ domainSuite <- getTestSuite+ run <- runWithCurrent + return $ do+ suite "Framework checks" $ do+ suite "Text encodings" $ do+ addProperty "UTF8 encoding" UTF8.propEncoding+ addProperty "JSON encoding" JSON.propEncoding+ addProperty "OpenMath encoding" OpenMath.propEncoding+ + suite "Domain checks" domainSuite+ + suite "Exercise checks" $+ forM_ pkgs $ \(Some pkg) ->+ exerciseTestSuite (exercise pkg)+ + suite "Black box tests" $ do + liftIO (blackBoxTests run dir) >>= id+ -- Returns the number of tests performed-unitTests :: String -> DomainReasoner Int-unitTests = visit 0- where- visit i path = do- valid <- liftIO $ doesDirectoryExist path- if not valid then return 0 else do- -- analyse content- xs <- liftIO $ getDirectoryContents path- let xml = filter (".xml" `isSuffixOf`) xs- json = filter (".json" `isSuffixOf`) xs- liftIO $ putStrLn $ replicate (i+1) '*' ++ " " ++ simplerDirectory path- -- perform tests- forM json $ \x -> - performUnitTest JSON (path ++ "/" ++ x)- forM xml $ \x -> - performUnitTest XML (path ++ "/" ++ x)- -- recursively visit subdirectories- is <- forM (filter ((/= ".") . take 1) xs) $ \x -> - visit (i+1) (path ++ "/" ++ x)- return (length (xml ++ json) + sum is)+blackBoxTests :: (DomainReasoner Bool -> IO Bool) -> String -> IO TestSuite+blackBoxTests run path = do+ putStrLn ("Scanning " ++ path)+ -- analyse content+ xs0 <- getDirectoryContents path+ let (xml, xs1) = partition (".xml" `isSuffixOf`) xs0+ (json, xs2) = partition (".json" `isSuffixOf`) xs1+ -- perform tests+ ts1 <- forM json $ \x ->+ doBlackBoxTest run JSON (path ++ "/" ++ x)+ ts2 <- forM xml $ \x ->+ doBlackBoxTest run XML (path ++ "/" ++ x)+ -- recursively visit subdirectories+ ts3 <- forM (filter ((/= ".") . take 1) xs2) $ \x -> do+ let p = path ++ "/" ++ x+ valid <- doesDirectoryExist p+ if not valid + then return (return ())+ else liftM (suite $ "Directory " ++ simplerDirectory p) + (blackBoxTests run p)+ return $ + sequence_ (ts1 ++ ts2 ++ ts3) -performUnitTest :: DataFormat -> FilePath -> DomainReasoner ()-performUnitTest format path = do- liftIO useFixedStdGen -- fix the random number generator- txt <- liftIO $ readFile path- exp <- liftIO $ readFile expPath- out <- case format of - JSON -> liftM snd3 (processJSON txt)- XML -> liftM snd3 (processXML txt) - `catchError` \_ -> return "Error"- liftIO $ reportTest (stripDirectoryPart path) (out ~= exp)+doBlackBoxTest :: (DomainReasoner Bool -> IO Bool) -> DataFormat -> FilePath -> IO TestSuite+doBlackBoxTest run format path = do+ b <- doesFileExist expPath+ return $ if not b + then warn $ expPath ++ " does not exist"+ else assertIO (stripDirectoryPart path) $ run $ do + -- Comparing output with expected output+ liftIO useFixedStdGen -- fix the random number generator+ txt <- liftIO $ readFile path+ expt <- liftIO $ readFile expPath+ out <- case format of + JSON -> liftM snd3 (processJSON txt)+ XML -> liftM snd3 (processXML txt)+ -- Conditional forces evaluation of the result, to make sure that+ -- all file handles are closed afterwards.+ if out ~= expt then return True else return False+ `catchError` + \_ -> return False where expPath = baseOf path ++ ".exp" baseOf = reverse . drop 1 . dropWhile (/= '.') . reverse@@ -118,33 +118,3 @@ rs = [ r | RewriteRule r <- concatMap transformations rwrs ] -- eqs = bothWays [ r | RewriteRule r <- concatMap transformations Logic.logicRules ] -}---- Helper functions-showDiffWith :: MonadIO m => (TimeDiff -> IO ()) -> m a -> m a-showDiffWith f action = do- t0 <- liftIO getClockTime- a <- action- t1 <- liftIO getClockTime- liftIO (f (diffClockTimes t1 t0))- return a--totalDiff :: MonadIO m => m a -> m a-totalDiff = showDiffWith (putStrLn . ("*** Total time: "++) . formatDiff)- -timeDiff :: MonadIO m => m a -> m a-timeDiff = showDiffWith (putStrLn . ("+++ Time: "++) . formatDiff) --formatDiff :: TimeDiff -> String-formatDiff d@(TimeDiff z1 z2 z3 h m s p)- | any (/=0) [z1,z2,z3] = timeDiffToString d- | s >= 60 = formatDiff (timeDiff ((h*60+m)*60+s) p)- | h==0 && m==0 = show inSec ++ " secs"- | otherwise = show (60*h+m) ++ ":" ++ digSec ++ " mins" - where- milSec = 1000*toInteger s + p `div` 1000000000- inSec = fromIntegral milSec / 1000- digSec = (if s < 10 then ('0' :) else id) (show s)- timeDiff n p = - let (rest, s) = n `divMod` 60- (h, m) = rest `divMod` 60- in TimeDiff 0 0 0 h m s p
src/Documentation/ServicePage.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS -XRankNTypes #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -13,29 +14,25 @@ import Documentation.DefaultPage import Service.ExercisePackage-import Service.ServiceList import Service.TypedExample import Service.Types import Service.DomainReasoner-import Service.TypedAbstractService (emptyState)+import Service.State import Text.HTML import qualified Text.XML as XML-import Text.XML (XML, showXML)-import Domain.Logic-import Domain.Math.Polynomial.Exercises-import Domain.Math.Data.Relation-import Domain.Math.Expr.Symbolic+import Text.XML (XML) import Control.Monad-import Common.Utils (ShowString(..))+import Common.Exercise+import Common.Utils (Some(..)) makeServicePage :: String -> Service -> DomainReasoner () makeServicePage dir s = do- xs <- examplesFor (serviceName s)+ xs <- examplesFor (showId s) generatePageAt 1 dir (servicePageFile s) (servicePage xs s) servicePage :: [Example] -> Service -> HTMLBuilder-servicePage examples s = do- h1 (serviceName s)+servicePage xs s = do+ h1 (showId s) para $ do bold $ text "Signature:"@@ -45,20 +42,20 @@ para $ do bold $ text "Description: " br- text $ serviceDescription s+ text $ description s when (serviceDeprecated s) $ para $ bold $ text "Warning: this service is deprecated!" - unless (null examples) $ do- h2 $ "XML examples (" ++ show (length examples) ++ ")"- forM_ (zip [1..] examples) $ + unless (null xs) $ do+ h2 $ "XML examples (" ++ show (length xs) ++ ")"+ forM_ (zip [1::Int ..] xs) $ \(i, (msg, (xmlRequest, xmlReply, xmlTest))) -> do h2 $ show i ++ ". " ++ msg bold $ text "Request:"- preText $ showXML xmlRequest+ highlightXML True xmlRequest bold $ text "Reply:"- preText $ showXML xmlReply+ highlightXML True xmlReply unless xmlTest $ XML.element "font" $ do "color" XML..=. "red"@@ -70,30 +67,53 @@ type Example = (String, (XML, XML, Bool)) examplesFor :: String -> DomainReasoner [Example]-examplesFor s = sequence [ m | (t, m) <- list, s == t ]+examplesFor s = tryAll [ f t | (t, f) <- list, s == t ] where list = - [ logic "derivation" [Nothing ::: Maybe StrategyCfg, stLogic1]- , lineq "derivation" [Nothing ::: Maybe StrategyCfg, stLineq1]- , logic "allfirsts" [stLogic2]- , lineq "allfirsts" [stLineq2]- , logic "onefirst" [stLogic2]- , lineq "onefirst" [stLineq2]--- , logic "applicable" [[] ::: Location, stLogic1]- , lineq "rulesinfo" []- , lineq "rulelist" [linearExercise ::: Exercise]- , lineq "strategyinfo" [linearExercise ::: Exercise]+ [ ("derivation", makeExample "logic.dnf" (noCfg +++ logic1))+ , ("derivation", makeExample "math.lineq" (noCfg +++ lineq1))+ , ("allfirsts", makeExample "logic.dnf" logic2)+ , ("allfirsts", makeExample "math.lineq" lineq2)+ , ("onefirst", makeExample "logic.dnf" logic2)+ , ("onefirst", makeExample "math.lineq" lineq2)+ , ("rulesinfo", makeExample "math.lineq" noArgs)+ , ("rulelist", makeExample "math.lineq" exArgs)+ , ("strategyinfo", makeExample "math.lineq" exArgs)+ , ("examples", makeExample "math.lineq" exArgs) ]- strVar = Var . ShowString- stLogic1 = emptyState dnfExercise (Not (strVar "p" :&&: Not (strVar "q"))) ::: State- stLogic2 = emptyState dnfExercise (Not (Not (strVar "p")) :&&: Not T) ::: State- stLineq1 = emptyState linearExercise (5*(variable "x"+1) :==: 11) ::: State- stLineq2 = emptyState linearExercise (5*(variable "x"+1) :==: (variable "x"-1)/2) ::: State - logic = make "Logic" (package dnfExercise)- lineq = make "Linear equation" (termPackage linearExercise)+ logic1, logic2 :: Args+ logic1 pkg = newState pkg "~(p /\\ ~q)"+ logic2 pkg = newState pkg "~~p /\\ T"+ + lineq1, lineq2 :: Args+ lineq1 pkg = newState pkg "5*(x+1) == 11"+ lineq2 pkg = newState pkg "5*(x+1) == (x-1)/2"+ + (f +++ g) pkg = f pkg ++ g pkg+ + noCfg _ = [Nothing ::: maybeTp StrategyCfg]+ noArgs _ = []+ exArgs pkg = [pkg ::: ExercisePkg]++tryAll :: [DomainReasoner a] -> DomainReasoner [a]+tryAll xs = + let f m = liftM return m `catchError` const (return [])+ in liftM concat (mapM f xs) - make msg pkg fs args = (fs, do- srv <- findService fs- tr <- typedExample pkg srv args- return (msg, tr))+newState :: Monad m => ExercisePackage a -> String -> m (TypedValue a)+newState pkg s = do+ let ex = exercise pkg+ case parser ex s of+ Left msg -> fail ("newState: " ++ msg)+ Right a -> return (emptyState pkg a ::: stateTp)+ +type Args = forall a . ExercisePackage a -> [TypedValue a]++makeExample :: String -> Args -> String -> DomainReasoner Example+makeExample pkgName f srvName = do+ let a = newId pkgName+ Some pkg <- findPackage a+ srv <- findService srvName+ tr <- typedExample pkg srv (f pkg)+ return (showId pkg, tr)
src/Documentation/TestsPage.hs view
@@ -9,80 +9,68 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Documentation.TestsPage (main) where+module Documentation.TestsPage (makeTestsPage) where -import Control.Monad-import Data.Char-import Data.List+import Common.TestSuite import Documentation.DefaultPage+import Documentation.SelfCheck import Service.DomainReasoner-import System.Environment-import Main.Revision import Text.HTML import qualified Text.XML as XML -main :: IO ()-main = do- args <- getArgs- case args of- [fileIn, fileOut] -> do- input <- readFile fileIn- runDomainReasoner $ do- setFullVersion fullVersion- generatePage "docs" (up 1 ++ fileOut) (testsPage input)- _ -> fail "Invalid invocation"--fullVersion :: String-fullVersion = "version " ++ version ++ " (revision " ++ show revision ++ ", " ++ lastChanged ++ ")"+makeTestsPage :: String -> String -> DomainReasoner ()+makeTestsPage docDir testDir = do+ checks <- selfCheck testDir+ result <- liftIO (runTestSuiteResult checks)+ generatePage docDir testsPageFile (testsPage result) -testsPage :: String -> HTMLBuilder-testsPage input = do +testsPage :: TestSuiteResult -> HTMLBuilder+testsPage result = do + h1 "Summary"+ preText (makeSummary result) h1 "Tests"- let (hs, bs) = unzip (map format (lines input))- bold (text "Failures: ") - text $ show $ length $ filter not bs- brs hs- where- format :: String -> (HTMLBuilder, Bool)- format s- | any (`elem` ws) ["failed", "error", "error:", "falsifiable"] =- (errorLine (ttText s), False)- | "* " `isPrefixOf` s =- (h2 (drop 2 s), True)- | "** " `isPrefixOf` s =- (br >> bold (text (drop 3 s)), True)- | "*** " `isPrefixOf` s =- (br >> bold (text (drop 4 s)), True)- | otherwise = - (fromString s, True)- where- ws = map (map toLower . filter isAlpha) (words s)- - -brs :: [HTMLBuilder] -> HTMLBuilder-brs = mapM_ (>> br)+ makeTestLogWith formatHTML result+ +formatHTML :: FormatLog HTMLBuilder+formatHTML = FormatLog+ { formatRoot = \_ -> id+ , formatSuite = \loc s _ _ a -> + showHeader loc s >> a + , formatSuccesses = \xs -> + let f (_, n) = if n==1 then "." else "(passed " ++ show n ++ " tests)"+ in mapM_ (\s -> ttText s >> br) (breakLine (concatMap f xs))+ , formatFailure = \s msg -> colorRed $ do+ bold (ttText ("Error" ++ putLabel s))+ tt space+ ttText msg+ br+ , formatWarning = \s msg -> colorOrange $ do+ ttText ("Warning" ++ putLabel s)+ tt space+ ttText msg+ br+ }+ where + putLabel s = if null s then ":" else " (" ++ s ++ "):" -fromString :: String -> HTMLBuilder-fromString = f []+breakLine :: String -> [String]+breakLine xs+ | null xs = []+ | otherwise = ys : breakLine zs where- f acc [] = ttText (reverse acc)- f acc list@(x:xs) - | "+++" `isPrefixOf` list = do- f acc [] - unless (null acc) (spaces 3)- okLine (ttText (drop 3 list))- | "*** Gave up!" `isPrefixOf` list = do- f acc []- unless (null acc) (spaces 3)- ttText (drop 3 list)- | otherwise = f (x:acc) xs+ (ys, zs) = splitAt 80 xs -errorLine :: HTMLBuilder -> HTMLBuilder-errorLine b = XML.element "font" $ do+showHeader :: [Int] -> String -> HTMLBuilder+showHeader [a] s = h2 (show a ++ ". " ++ s)+showHeader [a,b] s = h3 (show a ++ "." ++ show b ++ ". " ++ s)+showHeader _ s = para (bold (text s))++colorRed :: HTMLBuilder -> HTMLBuilder+colorRed body = XML.element "font" $ do "color" XML..=. "red"- bold b+ body -okLine :: HTMLBuilder -> HTMLBuilder-okLine b = XML.element "font" $ do- "color" XML..=. "gray"- b+colorOrange :: HTMLBuilder -> HTMLBuilder+colorOrange body = XML.element "font" $ do+ "color" XML..=. "#FE9A2E"+ body
src/Domain/LinearAlgebra/Checks.hs view
@@ -11,10 +11,10 @@ ----------------------------------------------------------------------------- module Domain.LinearAlgebra.Checks (checks) where -import Common.Apply +import Common.Classes import Common.Context import Common.Exercise -import Common.Utils +import Common.TestSuite import Domain.LinearAlgebra hiding (getSolution) import Domain.Math.Expr import Domain.Math.Simplification (simplify) @@ -23,13 +23,13 @@ ----------------------------------------------------------- --- QuickCheck properties -checks :: IO () -checks = do - putStrLn "** Linear algebra" - thoroughCheck propEchelon - thoroughCheck propReducedEchelon - thoroughCheck propSound - thoroughCheck propSolution +checks :: TestSuite +checks = suite "Linear algebra" $ do + let thorough = stdArgs {maxSize = 500, maxSuccess = 500} + addPropertyWith "echelon" thorough propEchelon + addPropertyWith "reduced echelon" thorough propReducedEchelon + addPropertyWith "sound" thorough propSound + addPropertyWith "solution" thorough propSolution propEchelon :: Matrix Rational -> Bool propEchelon =
src/Domain/LinearAlgebra/EquationsRules.hs view
@@ -16,6 +16,7 @@ import Common.Transformation import Common.Utils import Common.Navigator+import Common.Id import Common.View hiding (simplify) import Control.Monad import Data.List hiding (repeat)@@ -37,7 +38,8 @@ ] ruleExchangeEquations :: Rule (Context (LinearSystem Expr))-ruleExchangeEquations = simplifySystem $ makeRule "Exchange" $ +ruleExchangeEquations = describe "Exchange two equations" $ + simplifySystem $ makeRule "linearalgebra.linsystem.exchange" $ supplyLabeled2 descr args (\x y -> liftTransContext $ exchange x y) where descr = ("equation 1", "equation 2")@@ -49,7 +51,8 @@ return (cov, cov + i) ruleEliminateVar :: Rule (Context (LinearSystem Expr))-ruleEliminateVar = simplifySystem $ makeRule "Eliminate variable" $ +ruleEliminateVar = describe "Eliminate a variable (using addition)" $+ simplifySystem $ makeRule "linearalgebra.linsystem.eliminate" $ supplyLabeled3 descr args (\x y z -> liftTransContext $ addEquations x y z) where descr = ("equation 1", "equation 2", "scale factor")@@ -64,20 +67,23 @@ return (i + cov + 1, cov, v) ruleDropEquation :: Rule (Context (LinearSystem Expr))-ruleDropEquation = simplifySystem $ makeSimpleRule "Drop (0=0) equation" $ withCM $ \ls -> do- i <- findIndexM (fromMaybe False . testConstants (==)) ls- modifyVar covered (\n -> if i < n then n-1 else n)- return (deleteIndex i ls)+ruleDropEquation = describe "Drop trivial equations (such as 0=0)" $+ simplifySystem $ makeSimpleRule "linearalgebra.linsystem.trivial" $ withCM $ \ls -> do+ i <- findIndexM (fromMaybe False . testConstants (==)) ls+ modifyVar covered (\n -> if i < n then n-1 else n)+ return (deleteIndex i ls) ruleInconsistentSystem :: Rule (Context (LinearSystem Expr))-ruleInconsistentSystem = simplifySystem $ makeSimpleRule "Inconsistent system (0=1)" $ withCM $ \ls -> do- let stop = [0 :==: 1]- guard (invalidSystem ls && ls /= stop)- writeVar covered 1- return stop+ruleInconsistentSystem = describe "Inconsistent system (0=1)" $+ simplifySystem $ makeSimpleRule "linearalgebra.linsystem.inconsistent" $ withCM $ \ls -> do+ let stop = [0 :==: 1]+ guard (invalidSystem ls && ls /= stop)+ writeVar covered 1+ return stop ruleScaleEquation :: Rule (Context (LinearSystem Expr))-ruleScaleEquation = simplifySystem $ makeRule "Scale equation to one" $ +ruleScaleEquation = describe "Scale equation to one" $ + simplifySystem $ makeRule "linearalgebra.linsystem.scale" $ supplyLabeled2 descr args (\x y -> liftTransContext $ scaleEquation x y) where descr = ("equation", "scale factor")@@ -91,7 +97,8 @@ return (cov, coef) ruleBackSubstitution :: Rule (Context (LinearSystem Expr))-ruleBackSubstitution = simplifySystem $ makeRule "Back substitution" $ +ruleBackSubstitution = describe "Back substitution" $+ simplifySystem $ makeRule "linearalgebra.linsystem.subst" $ supplyLabeled3 descr args (\x y z -> liftTransContext $ addEquations x y z) where descr = ("equation 1", "equation 2", "scale factor")@@ -99,29 +106,33 @@ cov <- readVar covered eq <- maybeCM $ safeHead $ drop cov ls let expr = leftHandSide eq- mv <- maybeCM $ safeHead (getVars expr)+ mv <- maybeCM $ safeHead (vars expr) i <- findIndexM ((/= 0) . coefficientOf mv . leftHandSide) (take cov ls) let coef = negate $ coefficientOf mv (leftHandSide (ls !! i)) return (i, cov, coef) ruleIdentifyFreeVariables :: IsLinear a => Rule (Context (LinearSystem a))-ruleIdentifyFreeVariables = minorRule $ makeSimpleRule "Identify free variables" $ withCM $ \ls ->- let vars = [ head ys | ys <- map (getVars . leftHandSide) ls, not (null ys) ]- change eq =- let (e1, e2) = splitLinearExpr (`notElem` vars) (leftHandSide eq) -- constant ends up in e1+ruleIdentifyFreeVariables = describe "Identify free variables" $ + minorRule $ makeSimpleRule "linearalgebra.linsystem.freevars" $ withCM $ \ls ->+ let vs = [ head ys | ys <- map (vars . leftHandSide) ls, not (null ys) ]+ f eq =+ let (e1, e2) = splitLinearExpr (`notElem` vs) (leftHandSide eq) -- constant ends up in e1 in e2 :==: rightHandSide eq - e1- in return (map change ls)+ in return (map f ls) ruleCoverUpEquation :: Rule (Context (LinearSystem a))-ruleCoverUpEquation = minorRule $ makeRule "Cover up first equation" $ changeCover (+1)+ruleCoverUpEquation = describe "Cover up first equation" $ + minorRule $ makeRule "linearalgebra.linsystem.coverup" $ changeCover succ ruleUncoverEquation :: Rule (Context (LinearSystem a))-ruleUncoverEquation = minorRule $ makeRule "Uncover one equation" $ changeCover (\x -> x-1)+ruleUncoverEquation = describe "Uncover one equation" $ + minorRule $ makeRule "linearalgebra.linsystem.uncover" $ changeCover pred ruleCoverAllEquations :: Rule (Context (LinearSystem a))-ruleCoverAllEquations = minorRule $ makeSimpleRule "Cover all equations" $ withCM $ \ls -> do- writeVar covered (length ls)- return ls+ruleCoverAllEquations = describe "Cove all equations" $ + minorRule $ makeSimpleRule "linearalgebra.linsystem.coverall" $ withCM $ \ls -> do+ writeVar covered (length ls)+ return ls -- local helper functions deleteIndex :: Int -> [a] -> [a]@@ -130,7 +141,7 @@ testConstants :: IsLinear a => (a -> a -> Bool) -> Equation a -> Maybe Bool testConstants f (lhs :==: rhs)- | isConstant lhs && isConstant rhs = Just (f lhs rhs)+ | hasNoVar lhs && hasNoVar rhs = Just (f lhs rhs) | otherwise = Nothing -- simplify a linear system
src/Domain/LinearAlgebra/Exercises.hs view
@@ -14,7 +14,7 @@ , gaussianElimExercise, systemWithMatrixExercise ) where -import Common.Apply +import Common.Classes import Common.Context import Common.Exercise import Common.Transformation @@ -34,8 +34,8 @@ gramSchmidtExercise :: Exercise (VectorSpace (Simplified Expr)) gramSchmidtExercise = makeExercise - { description = "Gram-Schmidt" - , exerciseCode = makeCode "linalg" "gramschmidt" + { exerciseId = describe "Gram-Schmidt" $ + newId "linearalgebra.gramschmidt" , status = Alpha , parser = \s -> case parseVectorSpace s of Right a -> Right (fmap simplified a) @@ -52,8 +52,8 @@ linearSystemExercise :: Exercise (Equations Expr) linearSystemExercise = makeExercise - { description = "Solve Linear System" - , exerciseCode = makeCode "linalg" "linsystem" + { exerciseId = describe "Solve Linear System" $ + newId "linearalgebra.linsystem" , status = Stable , parser = \s -> case parseSystem s of Right a -> Right (simplify a) @@ -65,6 +65,7 @@ (Just a, Just b) -> getSolution a == getSolution b _ -> False , extraRules = equationsRules + , ruleOrdering = ruleOrderingWithId [getId ruleScaleEquation] , isReady = inSolvedForm , strategy = linearSystemStrategy , randomExercise = simpleGenerator (fmap matrixToSystem arbMatrix) @@ -72,8 +73,8 @@ gaussianElimExercise :: Exercise (Matrix Expr) gaussianElimExercise = makeExercise - { description = "Gaussian Elimination" - , exerciseCode = makeCode "linalg" "gaussianelim" + { exerciseId = describe "Gaussian Elimination" $ + newId "linearalgebra.gaussianelim" , status = Stable , parser = \s -> case parseMatrix s of Right a -> Right (simplify a) @@ -88,8 +89,8 @@ systemWithMatrixExercise :: Exercise Expr systemWithMatrixExercise = makeExercise - { description = "Solve Linear System with Matrix" - , exerciseCode = makeCode "linalg" "systemwithmatrix" + { exerciseId = describe "Solve Linear System with Matrix" $ + newId "linearalgebra.systemwithmatrix" , status = Provisional , parser = \s -> case (parser linearSystemExercise s, parser gaussianElimExercise s) of (Right ok, _) -> Right $ toExpr ok @@ -106,7 +107,7 @@ in case (f x, f y) of (Just a, Just b) -> equivalence linearSystemExercise a b _ -> False - , extraRules = map liftExpr equationsRules ++ map liftExpr (matrixRules :: [Rule (Context (Matrix Expr))]) + , extraRules = map useC equationsRules ++ map useC (matrixRules :: [Rule (Context (Matrix Expr))]) , isReady = inSolvedForm . (fromExpr :: Expr -> Equations Expr) , strategy = systemWithMatrixStrategy , randomExercise = simpleGenerator (fmap (toExpr . matrixToSystem) (arbMatrix :: Gen (Matrix Expr))) @@ -115,36 +116,10 @@ -------------------------------------------------------------- -- Other stuff (to be cleaned up) - -instance Arbitrary a => Arbitrary (Vector a) where - arbitrary = liftM fromList $ oneof $ map vector [0..2] -instance CoArbitrary a => CoArbitrary (Vector a) where - coarbitrary = coarbitrary . toList -instance Arbitrary a => Arbitrary (VectorSpace a) where - arbitrary = do - i <- choose (0, 3) -- too many vectors "disables" prime factorization - j <- choose (0, 10 `div` i) - xs <- replicateM i (liftM fromList $ replicateM j arbitrary) - return $ makeVectorSpace xs -instance CoArbitrary a => CoArbitrary (VectorSpace a) where - coarbitrary = coarbitrary . vectors - arbMatrix :: Num a => Gen (Matrix a) arbMatrix = fmap (fmap fromInteger) arbNiceMatrix - -instance Arbitrary a => Arbitrary (Matrix a) where - arbitrary = do - (i, j) <- arbitrary - arbSizedMatrix (i `mod` 5, j `mod` 5) -instance CoArbitrary a => CoArbitrary (Matrix a) where - coarbitrary = coarbitrary . rows -arbSizedMatrix :: Arbitrary a => (Int, Int) -> Gen (Matrix a) -arbSizedMatrix (i, j) = - do rows <- replicateM i (vector j) - return (makeMatrix rows) - arbUpperMatrix :: (Enum a, Num a) => Gen (Matrix a) arbUpperMatrix = do a <- oneof $ map return [-5 .. 5]
src/Domain/LinearAlgebra/LinearSystem.hs view
@@ -16,41 +16,44 @@ import Domain.LinearAlgebra.LinearView import Data.List import Data.Maybe +import Common.Classes import Control.Monad import Common.Utils import Common.Uniplate +import Common.Rewriting +import qualified Data.Set as S type LinearSystem a = Equations a getVarsSystem :: IsLinear a => LinearSystem a -> [String] -getVarsSystem = foldr (\(lhs :==: rhs) xs -> getVars lhs `union` getVars rhs `union` xs) [] +getVarsSystem = S.toList . S.unions . map varSet . concatMap crush evalSystem :: (Uniplate a, IsLinear a) => (String -> a) -> LinearSystem a -> Bool evalSystem f = - let eval (x :==: y) = x==y - in all (eval . fmap (evalLinearExpr f)) + let evalEq (x :==: y) = x==y + in all (evalEq . fmap (evalLinearExpr f)) invalidSystem :: IsLinear a => LinearSystem a -> Bool invalidSystem = any invalidEquation invalidEquation :: IsLinear a => Equation a -> Bool -invalidEquation (lhs :==: rhs) = null (getVars lhs ++ getVars rhs) && getConstant lhs /= getConstant rhs +invalidEquation (lhs :==: rhs) = hasNoVar lhs && hasNoVar rhs && getConstant lhs /= getConstant rhs getSolution :: IsLinear a => LinearSystem a -> Maybe [(String, a)] getSolution xs = do - guard (distinct vars) - guard (null (vars `intersect` frees)) + guard (distinct vs) + guard (null (vs `intersect` frees)) mapM make xs where - vars = concatMap (getVars . leftHandSide) xs - frees = concatMap (getVars . rightHandSide) xs + vs = concatMap (vars . leftHandSide) xs + frees = concatMap (vars . rightHandSide) xs make (lhs :==: rhs) = do - v <- isVar lhs + v <- getVariable lhs return (v, rhs) -- No constant on the left, no variables on the right inStandardForm :: IsLinear a => Equation a -> Bool -inStandardForm (lhs :==: rhs) = getConstant lhs == 0 && null (getVars rhs) +inStandardForm (lhs :==: rhs) = getConstant lhs == 0 && hasNoVar rhs toStandardForm :: IsLinear a => Equation a -> Equation a toStandardForm (lhs :==: rhs) = @@ -66,11 +69,11 @@ -- Conversions systemToMatrix :: IsLinear a => LinearSystem a -> (Matrix a, [String]) -systemToMatrix system = (makeMatrix $ map (makeRow . toStandardForm) system, vars) +systemToMatrix system = (makeMatrix $ map (makeRow . toStandardForm) system, vs) where - vars = getVarsSystem system + vs = getVarsSystem system makeRow (lhs :==: rhs) = - map (`coefficientOf` lhs) vars ++ [getConstant rhs] + map (`coefficientOf` lhs) vs ++ [getConstant rhs] matrixToSystem :: IsLinear a => Matrix a -> LinearSystem a matrixToSystem = matrixToSystemWith variables @@ -81,7 +84,7 @@ varList = vs ++ (variables \\ vs) makeEquation [] = 0 :==: 0 makeEquation xs = - let lhs = sum (zipWith (\v a -> a * var v) varList (init xs)) + let lhs = sum (zipWith (\v a -> a * variable v) varList (init xs)) rhs = last xs in lhs :==: rhs
src/Domain/LinearAlgebra/LinearView.hs view
@@ -10,16 +10,16 @@ -- ----------------------------------------------------------------------------- module Domain.LinearAlgebra.LinearView - ( IsLinear(..), var, isVar, isConstant, renameVariables + ( IsLinear(..), LinearMap, renameVariables , splitLinearExpr, evalLinearExpr, linearView - , LinearMap ) where import Control.Monad import Data.List +import Common.Rewriting import Common.Uniplate -import Common.View hiding (simplify) -import Domain.Math.Expr hiding (isVariable) +import Common.View +import Domain.Math.Expr import qualified Data.Map as M data LinearMap a = LM { lmMap :: M.Map String a, lmConstant :: a } @@ -73,73 +73,35 @@ guard (M.null m) return $ LM M.empty (sqrt c) -symLM :: Symbolic a => Symbol -> [LinearMap a] -> Maybe (LinearMap a) +symLM :: WithFunctions a => Symbol -> [LinearMap a] -> Maybe (LinearMap a) symLM f ps = do guard (all (M.null . lmMap) ps) return $ LM M.empty (function f (map lmConstant ps)) -class (Fractional a, Symbolic a) => IsLinear a where - isLinear :: a -> Bool - isVariable :: a -> Maybe String - getVars :: a -> [String] - getConstant :: a -> a - coefficientOf :: String -> a -> a +class (Fractional a, Uniplate a, WithVars a) => IsLinear a where + isLinear :: a -> Bool + getConstant :: a -> a + coefficientOf :: String -> a -> a instance IsLinear Expr where - - isLinear expr = belongsTo expr linearView - - isVariable expr = - case expr of - Var s -> Just s - _ -> Nothing - - getVars = collectVars - - getConstant expr = - case match linearView expr of - Just (LM _ c) -> c - _ -> 0 - - coefficientOf s expr = - case match linearView expr of - Just (LM m _) -> M.findWithDefault 0 s m - _ -> 0 - -{- instance IsLinear SExpr where - isLinear = isLinear . toExpr - isVariable = isVariable . toExpr - getVars = getVars . toExpr - getConstant = simplifyExpr . getConstant . toExpr - coefficientOf s = simplifyExpr . coefficientOf s . toExpr -} + isLinear = (`belongsTo` linearView) + getConstant = maybe 0 lmConstant . match linearView + coefficientOf s = maybe 0 (M.findWithDefault 0 s . lmMap) . match linearView splitLinearExpr :: IsLinear a => (String -> Bool) -> a -> (a, a) splitLinearExpr f a = (make (getConstant a) xs, make 0 ys) where - (xs, ys) = partition f (getVars a) - make = foldr (\v r -> coefficientOf v a * var v + r) + (xs, ys) = partition f (vars a) + make = foldr (\v r -> coefficientOf v a * variable v + r) -evalLinearExpr :: (IsLinear a, Uniplate a) => (String -> a) -> a -> a +evalLinearExpr :: IsLinear a => (String -> a) -> a -> a evalLinearExpr f a = - case isVariable a of + case getVariable a of Just s -> f s - Nothing -> g $ map (evalLinearExpr f) cs - where - (cs, g) = uniplate a + Nothing -> descend (evalLinearExpr f) a -renameVariables :: (IsLinear a, Uniplate a) => (String -> String) -> a -> a +renameVariables :: IsLinear a => (String -> String) -> a -> a renameVariables f a = - case isVariable a of + case getVariable a of Just s -> variable (f s) - Nothing -> g $ map (renameVariables f) cs - where - (cs, g) = uniplate a - -isConstant :: IsLinear a => a -> Bool -isConstant = null . getVars - -var :: IsLinear a => String -> a -var = variable - -isVar :: IsLinear a => a -> Maybe String -isVar = isVariable+ Nothing -> descend (renameVariables f) a
src/Domain/LinearAlgebra/Matrix.hs view
@@ -21,10 +21,15 @@ , isSquare, identityMatrix, isLowerTriangular, isUpperTriangular ) where +import Common.Classes +import Common.Rewriting hiding (inverse) import Control.Monad -import Data.Maybe import Data.List hiding (transpose) -import Common.Traversable +import Data.Maybe +import Domain.Math.Simplification +import Domain.Math.Expr.Symbols (openMathSymbol) +import Test.QuickCheck +import qualified Text.OpenMath.Dictionary.Linalg2 as OM import qualified Data.List as L import qualified Data.Map as M @@ -36,16 +41,42 @@ type Column a = [a] instance Functor Matrix where - fmap f (M rows) = M (map (map f) rows) - -instance Once Matrix where - onceM f (M xss) = do - yss <- onceM (onceM f) xss - return (M yss) + fmap f (M rs) = M (map (map f) rs) instance Switch Matrix where switch (M xss) = liftM M (mapM sequence xss) +instance IsTerm a => IsTerm (Matrix a) where + toTerm = + let f = function matrixrowSymbol . map toTerm + in function matrixSymbol . map f . rows + fromTerm a = do + rs <- isFunction matrixSymbol a + xss <- mapM (isFunction matrixrowSymbol) rs + yss <- mapM (mapM fromTerm) xss + guard (isRectangular yss) + return (makeMatrix yss) + +instance Arbitrary a => Arbitrary (Matrix a) where + arbitrary = do + (i, j) <- arbitrary + arbSizedMatrix (i `mod` 5, j `mod` 5) + +instance CoArbitrary a => CoArbitrary (Matrix a) where + coarbitrary = coarbitrary . rows + +arbSizedMatrix :: Arbitrary a => (Int, Int) -> Gen (Matrix a) +arbSizedMatrix (i, j) = + do rs <- replicateM i (vector j) + return (makeMatrix rs) + +matrixSymbol, matrixrowSymbol :: Symbol +matrixSymbol = openMathSymbol OM.matrixSymbol +matrixrowSymbol = openMathSymbol OM.matrixrowSymbol + +instance Simplify a => Simplify (Matrix a) where + simplifyWith opt = fmap (simplifyWith opt) + -- Check whether the table is rectangular isRectangular :: [[a]] -> Bool isRectangular xss = @@ -55,10 +86,10 @@ -- Constructor function that checks whether the table is rectangular makeMatrix :: [Row a] -> Matrix a -makeMatrix rows - | null (concat rows) = M [] - | isRectangular rows = M rows - | otherwise = error "makeMatrix: not rectangular" +makeMatrix rs + | null (concat rs) = M [] + | isRectangular rs = M rs + | otherwise = error "makeMatrix: not rectangular" identity :: Num a => Int -> Matrix a identity n = M $ map f [0..n-1] @@ -68,7 +99,7 @@ isEmpty (M xs) = null xs rows :: Matrix a -> [Row a] -rows (M rows) = rows +rows (M rs) = rs row :: Int -> Matrix a -> Row a row n = (!!n) . rows @@ -86,7 +117,7 @@ entry (i, j) m = row i m !! j mapWithPos :: ((Int, Int) -> a -> b) -> Matrix a -> Matrix b -mapWithPos f (M rows) = M $ zipWith g [0..] rows +mapWithPos f (M rs) = M $ zipWith g [0..] rs where g y = zipWith (\x -> f (y, x)) [0..] changeEntries :: M.Map (Int, Int) (a -> a) -> Matrix a -> Matrix a @@ -176,7 +207,7 @@ ------------------------------------------------------- transpose :: Matrix a -> Matrix a -transpose (M rows) = M (L.transpose rows) +transpose (M rs) = M (L.transpose rs) ------------------------------------------------------- @@ -195,30 +226,30 @@ checkRow i m = i >= 0 && i < fst (dimensions m) switchRows :: Int -> Int -> Matrix a -> Matrix a -switchRows i j m@(M rows) +switchRows i j m@(M rs) | i == j = m | i > j = switchRows j i m | checkRow i m && checkRow j m = - let (before, r1:rest) = splitAt i rows + let (before, r1:rest) = splitAt i rs (middle, r2:after) = splitAt (j-i-1) rest in M $ before ++ [r2] ++ middle ++ [r1] ++ after | otherwise = error "switchRows: invalid rows" scaleRow :: Num a => Int -> a -> Matrix a -> Matrix a -scaleRow i a m@(M rows) +scaleRow i a m@(M rs) | checkRow i m = let f y = if y==i then map (*a) else id - in M $ zipWith f [0..] rows + in M $ zipWith f [0..] rs | otherwise = error "scaleRow: invalid row" addRow :: Num a => Int -> Int -> a -> Matrix a -> Matrix a -addRow i j a m@(M rows) +addRow i j a m@(M rs) | checkRow i m && checkRow j m = let rj = map (*a) (row j m) f y = if y==i then zipWith (+) rj else id - in M $ zipWith f [0..] rows + in M $ zipWith f [0..] rs | otherwise = error "addRow: invalid row" @@ -233,9 +264,9 @@ where check n = all (==0) . take n inRowEchelonForm :: Num a => Matrix a -> Bool -inRowEchelonForm (M rows) = - null (filter nonZero (dropWhile nonZero rows)) && - increasing (map (length . takeWhile (==0)) (filter nonZero rows)) +inRowEchelonForm (M rs) = + null (filter nonZero (dropWhile nonZero rs)) && + increasing (map (length . takeWhile (==0)) (filter nonZero rs)) where increasing (x:ys@(y:_)) = x < y && increasing ys increasing _ = True @@ -245,10 +276,10 @@ -- or row canonical form inRowReducedEchelonForm :: Num a => Matrix a -> Bool -inRowReducedEchelonForm m@(M rows) = +inRowReducedEchelonForm m@(M rs) = inRowEchelonForm m && - all (==1) (mapMaybe pivot rows) && - all (isPivotColumn . flip column m . length . takeWhile (==0)) (filter nonZero rows) + all (==1) (mapMaybe pivot rs) && + all (isPivotColumn . flip column m . length . takeWhile (==0)) (filter nonZero rs) pivot :: Num a => Row a -> Maybe a pivot r = case dropWhile (==0) r of
src/Domain/LinearAlgebra/MatrixRules.hs view
@@ -13,7 +13,6 @@ import Domain.Math.Simplification import Domain.LinearAlgebra.Matrix -import Domain.LinearAlgebra.Symbols () import Common.Context import Common.Navigator import Common.Transformation @@ -29,7 +28,7 @@ ] ruleFindColumnJ :: Num a => Rule (Context (Matrix a)) -ruleFindColumnJ = minorRule $ makeSimpleRule "FindColumnJ" $ withCM $ \m -> do +ruleFindColumnJ = minorRule $ makeSimpleRule "linearalgebra.gaussianelim.FindColumnJ" $ withCM $ \m -> do cols <- liftM columns (subMatrix m) i <- findIndexM nonZero cols writeVar columnJ i @@ -76,24 +75,24 @@ return (k, cov, v) ruleCoverRow :: Rule (Context (Matrix a)) -ruleCoverRow = minorRule $ makeRule "CoverRow" $ changeCover (+1) +ruleCoverRow = minorRule $ makeRule "linearalgebra.gaussianelim.CoverRow" $ changeCover succ ruleUncoverRow :: Rule (Context (Matrix a)) -ruleUncoverRow = minorRule $ makeRule "UncoverRow" $ changeCover (\x -> x-1) +ruleUncoverRow = minorRule $ makeRule "linearalgebra.gaussianelim.UncoverRow" $ changeCover pred --------------------------------------------------------------------------------- -- Parameterized rules ruleScaleRow :: (Argument a, Fractional a) => (Context (Matrix a) -> Maybe (Int, a)) -> Rule (Context (Matrix a)) -ruleScaleRow f = makeRule "Scale" (supplyLabeled2 descr f rowScale) +ruleScaleRow f = makeRule "linearalgebra.gaussianelim.scale" (supplyLabeled2 descr f rowScale) where descr = ("row", "scale factor") ruleExchangeRows :: Num a => (Context (Matrix a) -> Maybe (Int, Int)) -> Rule (Context (Matrix a)) -ruleExchangeRows f = makeRule "Exchange" (supplyLabeled2 descr f rowExchange) +ruleExchangeRows f = makeRule "linearalgebra.gaussianelim.exchange" (supplyLabeled2 descr f rowExchange) where descr = ("row 1", "row 2") ruleAddMultiple :: (Argument a, Fractional a) => (Context (Matrix a) -> Maybe (Int, Int, a)) -> Rule (Context (Matrix a)) -ruleAddMultiple f = makeRule "Add" (supplyLabeled3 descr f rowAdd) +ruleAddMultiple f = makeRule "linearalgebra.gaussianelim.add" (supplyLabeled3 descr f rowAdd) where descr = ("row 1", "row2", "scale factor") --------------------------------------------------------------------------------- @@ -141,7 +140,7 @@ subMatrix :: Matrix a -> ContextMonad (Matrix a) subMatrix m = do cov <- readVar covered - return $ makeMatrix $ drop cov $ rows $ m + return $ makeMatrix $ drop cov $ rows m findIndexM :: MonadPlus m => (a -> Bool) -> [a] -> m Int findIndexM p = maybe mzero return . findIndex p
src/Domain/LinearAlgebra/Strategies.hs view
@@ -13,24 +13,20 @@ ( gaussianElimStrategy, linearSystemStrategy , gramSchmidtStrategy, systemWithMatrixStrategy , forwardPass - , liftExpr ) where import Prelude hiding (repeat) import Domain.Math.Expr -import Common.Rewriting import Domain.Math.Simplification import Domain.LinearAlgebra.Matrix import Domain.LinearAlgebra.MatrixRules import Domain.LinearAlgebra.EquationsRules import Domain.LinearAlgebra.GramSchmidtRules import Domain.LinearAlgebra.LinearSystem -import Domain.LinearAlgebra.Symbols () -import Common.Apply -import Common.Navigator import Common.Strategy hiding (not) import Common.Transformation import Common.Context +import Common.Id import Domain.LinearAlgebra.Vector gaussianElimStrategy :: LabeledStrategy (Context (Matrix Expr)) @@ -92,11 +88,11 @@ systemWithMatrixStrategy :: LabeledStrategy (Context Expr) systemWithMatrixStrategy = label "General solution to a linear system (matrix approach)" $ - repeat (mapRules liftExpr dropEquation) + repeat (mapRules useC dropEquation) <*> conv1 - <*> mapRules liftExpr gaussianElimStrategy + <*> mapRules useC gaussianElimStrategy <*> conv2 - <*> repeat (mapRules liftExpr dropEquation) + <*> repeat (mapRules useC dropEquation) gramSchmidtStrategy :: LabeledStrategy (Context (VectorSpace (Simplified Expr))) gramSchmidtStrategy = @@ -105,29 +101,24 @@ <*> label "Make vector orthogonal" (repeat (ruleNextOrthogonal <*> try ruleOrthogonal)) <*> label "Normalize" (try ruleNormalize) -vars :: Var [String] -vars = newVar "variables" [] +varVars :: Var [String] +varVars = newVar "variables" [] simplifyFirst :: Rule (Context (LinearSystem Expr)) simplifyFirst = simplifySystem idRule conv1 :: Rule (Context Expr) -conv1 = makeSimpleRule "Linear system to matrix" $ withCM $ \expr -> do - ls <- fromExpr expr - let (m, vs) = systemToMatrix ls - writeVar vars vs - return (toExpr (simplify (m :: Matrix Expr))) +conv1 = describe "Convert linear system to matrix" $ + makeSimpleRule "linearalgebra.linsystem.tomatrix" $ withCM $ \expr -> do + ls <- fromExpr expr + let (m, vs) = systemToMatrix ls + writeVar varVars vs + return (toExpr (simplify (m :: Matrix Expr))) conv2 :: Rule (Context Expr) -conv2 = makeSimpleRule "Matrix to linear system" $ withCM $ \expr -> do - vs <- readVar vars - m <- fromExpr expr - let linsys = matrixToSystemWith vs (m :: Matrix Expr) - a <- fromContext $ applyD simplifyFirst $ newContext emptyEnv (noNavigator linsys) -- !! - return $ toExpr a - -liftExpr :: IsTerm a => Rule (Context a) -> Rule (Context Expr) -liftExpr r = makeSimpleRuleList (name r) $ \a -> do - b <- castT exprView a - c <- applyAll r b - castT exprView c+conv2 = describe "Convert matrix to linear system" $ + makeSimpleRule "linearalgebra.linsystem.frommatrix" $ withCM $ \expr -> do + vs <- readVar varVars + m <- fromExpr expr + let linsys = matrixToSystemWith vs (m :: Matrix Expr) + return $ simplify $ toExpr linsys
− src/Domain/LinearAlgebra/Symbols.hs
@@ -1,65 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.LinearAlgebra.Symbols () where--import Domain.Math.Expr.Symbolic-import Domain.Math.Simplification-import Domain.LinearAlgebra.Matrix-import Domain.LinearAlgebra.Vector-import Control.Monad-import qualified Text.OpenMath.Dictionary.Linalg2 as Linalg2-import Common.Rewriting.Term--vectorSymbol, matrixSymbol, matrixrowSymbol :: Symbol-vectorSymbol = toSymbol Linalg2.vectorSymbol-matrixSymbol = toSymbol Linalg2.matrixSymbol-matrixrowSymbol = toSymbol Linalg2.matrixrowSymbol------------------------------------------------------------ Conversion to the Expr data type--instance IsTerm a => IsTerm (Matrix a) where- toTerm = - let f = function matrixrowSymbol . map toTerm- in function matrixSymbol . map f . rows- fromTerm a = do- rs <- isSymbol matrixSymbol a- xss <- mapM (isSymbol matrixrowSymbol) rs- yss <- mapM (mapM fromTerm) xss- guard (isRectangular yss)- return (makeMatrix yss)--instance IsTerm a => IsTerm (Vector a) where- toTerm = function vectorSymbol . map toTerm . toList- fromTerm a = do- xs <- isSymbol vectorSymbol a- ys <- mapM fromTerm xs- return (fromList ys)- -instance IsTerm a => IsTerm (VectorSpace a) where- toTerm = toTerm . vectors- fromTerm a = do- xs <- fromTerm a- guard (sameDimension xs)- return (makeVectorSpace xs)------------------------------------------------------------ Simplification--instance Simplify a => Simplify (Matrix a) where- simplify = fmap simplify--instance Simplify a => Simplify (Vector a) where- simplify = fmap simplify- -instance Simplify a => Simplify (VectorSpace a) where- simplify = fmap simplify
src/Domain/LinearAlgebra/Vector.hs view
@@ -18,8 +18,13 @@ ) where import Control.Monad -import Common.Traversable +import Common.Classes +import Common.Rewriting import Data.List +import Domain.Math.Simplification +import Domain.Math.Expr.Symbols (openMathSymbol) +import Test.QuickCheck +import qualified Text.OpenMath.Dictionary.Linalg2 as OM ------------------------------------------------------------------------------- -- Data types @@ -36,9 +41,6 @@ instance Functor Vector where fmap f (V xs) = V (map f xs) -instance Once Vector where - onceM f (V xs) = liftM V (onceM f xs) - instance Switch Vector where switch (V xs) = liftM V (switch xs) @@ -54,12 +56,50 @@ signum = liftV signum fromInteger = fromList . return . fromInteger +instance IsTerm a => IsTerm (Vector a) where + toTerm = function vectorSymbol . map toTerm . toList + fromTerm a = do + xs <- isFunction vectorSymbol a + ys <- mapM fromTerm xs + return (fromList ys) + +instance Arbitrary a => Arbitrary (Vector a) where + arbitrary = liftM fromList $ oneof $ map vector [0..2] + +instance CoArbitrary a => CoArbitrary (Vector a) where + coarbitrary = coarbitrary . toList + +vectorSymbol :: Symbol +vectorSymbol = openMathSymbol OM.vectorSymbol + +instance Simplify a => Simplify (Vector a) where + simplifyWith opt = fmap (simplifyWith opt) + instance Functor VectorSpace where fmap f (VS xs) = VS (map (fmap f) xs) instance Show a => Show (VectorSpace a) where show = show . vectors +instance IsTerm a => IsTerm (VectorSpace a) where + toTerm = toTerm . vectors + fromTerm a = do + xs <- fromTerm a + guard (sameDimension xs) + return (makeVectorSpace xs) + +instance Simplify a => Simplify (VectorSpace a) where + simplifyWith opt = fmap (simplifyWith opt) + +instance Arbitrary a => Arbitrary (VectorSpace a) where + arbitrary = do + i <- choose (0, 3) -- too many vectors "disables" prime factorization + j <- choose (0, 10 `div` i) + xs <- replicateM i (liftM fromList $ replicateM j arbitrary) + return $ makeVectorSpace xs +instance CoArbitrary a => CoArbitrary (VectorSpace a) where + coarbitrary = coarbitrary . vectors + ------------------------------------------------------------------------------- -- Vector Space operations @@ -125,7 +165,7 @@ orthonormalList :: Floating a => [Vector a] -> Bool orthonormalList xs = all isUnit xs && all (uncurry orthogonal) pairs where - pairs = [ (a, b) | (i, a) <- zip [0..] xs, (j, b) <- zip [0..] xs, i < j ] + pairs = [ (a, b) | (i, a) <- zip [0::Int ..] xs, (j, b) <- zip [0..] xs, i < j ] -- length of the vector (also called norm) norm :: Floating a => Vector a -> a
src/Domain/Logic.hs view
@@ -10,22 +10,21 @@ -- ----------------------------------------------------------------------------- module Domain.Logic - ( module Domain.Logic.Formula + ( module Domain.Logic.BuggyRules + , module Domain.Logic.Exercises + , module Domain.Logic.Formula + , module Domain.Logic.GeneralizedRules , module Domain.Logic.Generator , module Domain.Logic.Parser - , module Domain.Logic.Strategies , module Domain.Logic.Rules - , module Domain.Logic.BuggyRules - , module Domain.Logic.GeneralizedRules - , module Domain.Logic.Exercises + , module Domain.Logic.Strategies ) where - + +import Domain.Logic.BuggyRules hiding (rule, ruleList) +import Domain.Logic.Exercises import Domain.Logic.Formula +import Domain.Logic.GeneralizedRules import Domain.Logic.Generator import Domain.Logic.Parser -import Domain.Logic.Strategies -import Domain.Logic.Rules -import Domain.Logic.BuggyRules -import Domain.Logic.GeneralizedRules -import Domain.Logic.Exercises - +import Domain.Logic.Rules hiding (rule, ruleList) +import Domain.Logic.Strategies
src/Domain/Logic/BuggyRules.hs view
@@ -15,13 +15,15 @@ import Domain.Logic.Formula import Domain.Logic.Generator()-import Domain.Logic.Rules (makeGroup)+import Domain.Logic.Rules (makeGroup, logic)+import Common.Id import Common.Rewriting-import Common.Transformation+import Common.Transformation (Rule, buggyRule)+import qualified Common.Transformation as Rule -- Collection of all known buggy rules buggyRules :: [Rule SLogic]-buggyRules = makeGroup "Common misconceptions"+buggyRules = snd $ makeGroup "Common misconceptions" [ buggyRuleCommImp, buggyRuleAssImp, buggyRuleIdemImp, buggyRuleIdemEqui , buggyRuleEquivElim1, buggyRuleImplElim2, buggyRuleEquivElim2, buggyRuleEquivElim3 , buggyRuleImplElim, buggyRuleImplElim1, buggyRuleDeMorgan1, buggyRuleDeMorgan2, buggyRuleDeMorgan3@@ -31,6 +33,12 @@ , buggyRuleTrueProp, buggyRuleFalseProp, buggyRuleDistr, buggyRuleDistrNot ] +rule :: (RuleBuilder f a, Rewrite a) => String -> f -> Rule a+rule = Rule.rule . logic . ( "buggy" # )++ruleList :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a+ruleList = Rule.ruleList . logic . ( "buggy" # )+ ----------------------------------------------------------------------------- -- Buggy rules @@ -39,7 +47,7 @@ \x -> x :&&: x :~> T buggyRuleAndCompl :: Rule SLogic-buggyRuleAndCompl = buggyRule $ ruleList "AndComplBuggy"+buggyRuleAndCompl = buggyRule $ ruleList "AndCompl" [ \x -> x :&&: Not x :~> T , \x -> Not x :&&: x :~> T , \x -> x :&&: Not x :~> x@@ -51,7 +59,7 @@ \x -> x :||: x :~> T buggyRuleOrCompl :: Rule SLogic-buggyRuleOrCompl = buggyRule $ ruleList "OrComplBuggy"+buggyRuleOrCompl = buggyRule $ ruleList "OrCompl" [ \x -> x :||: Not x :~> F , \x -> Not x :||: x :~> F , \x -> x :||: Not x :~> x@@ -93,7 +101,7 @@ \x -> x :<->: x :~> x buggyRuleEquivElim1 :: Rule SLogic-buggyRuleEquivElim1 = buggyRule $ ruleList "BuggyEquivElim1"+buggyRuleEquivElim1 = buggyRule $ ruleList "EquivElim1" [ \x y -> x :<->: y :~> (x :&&: y) :||: Not (x :&&: y) , \x y -> x :<->: y :~> (x :&&: y) :||: (Not x :&&: y) , \x y -> x :<->: y :~> (x :&&: y) :||: ( x :&&: Not y)@@ -102,7 +110,7 @@ ] buggyRuleEquivElim2 :: Rule SLogic-buggyRuleEquivElim2 = buggyRule $ ruleList "BuggyEquivElim2"+buggyRuleEquivElim2 = buggyRule $ ruleList "EquivElim2" [ \x y -> x :<->: y :~> (x :||: y) :&&: (Not x :||: Not y) , \x y -> x :<->: y :~> (x :&&: y) :&&: (Not x :&&: Not y) , \x y -> x :<->: y :~> (x :&&: y) :||: (Not x :||: Not y)@@ -113,22 +121,22 @@ \x y -> x :<->: y :~> Not x :||: y buggyRuleImplElim :: Rule SLogic-buggyRuleImplElim = buggyRule $ ruleList "BuggyImplElim" +buggyRuleImplElim = buggyRule $ ruleList "ImplElim" [\x y -> x :->: y :~> Not (x :||: y) ,\x y -> x :->: y :~> (x :||: y) ,\x y -> x :->: y :~> Not (x :&&: y) ] buggyRuleImplElim1 :: Rule SLogic-buggyRuleImplElim1 = buggyRule $ rule "BuggyImplElim1" $ +buggyRuleImplElim1 = buggyRule $ rule "ImplElim1" $ \x y -> x :->: y :~> Not x :&&: y buggyRuleImplElim2 :: Rule SLogic-buggyRuleImplElim2 = buggyRule $ rule "BuggyImplElim2" $ +buggyRuleImplElim2 = buggyRule $ rule "ImplElim2" $ \x y -> x :->: y :~> (x :&&: y) :||: (Not x :&&: Not y) buggyRuleDeMorgan1 :: Rule SLogic-buggyRuleDeMorgan1 = buggyRule $ ruleList "BuggyDeMorgan1"+buggyRuleDeMorgan1 = buggyRule $ ruleList "DeMorgan1" [ \x y -> Not (x :&&: y) :~> Not x :||: y , \x y -> Not (x :&&: y) :~> x :||: Not y , \x y -> Not (x :&&: y) :~> x :||: y@@ -138,20 +146,20 @@ ] buggyRuleDeMorgan2 :: Rule SLogic-buggyRuleDeMorgan2 = buggyRule $ ruleList "BuggyDeMorgan2"+buggyRuleDeMorgan2 = buggyRule $ ruleList "DeMorgan2" [ \x y -> Not (x :&&: y) :~> Not (Not x :||: Not y) , \x y -> Not (x :||: y) :~> Not (Not x :&&: Not y) --note the firstNot in both formulas! ] buggyRuleDeMorgan3 :: Rule SLogic -buggyRuleDeMorgan3 = buggyRule $ rule "BuggyDeMorgan3" $+buggyRuleDeMorgan3 = buggyRule $ rule "DeMorgan3" $ \x y -> Not (x :&&: y) :~> Not x :&&: Not y buggyRuleDeMorgan4 :: Rule SLogic -buggyRuleDeMorgan4 = buggyRule $ rule "BuggyDeMorgan4" $ +buggyRuleDeMorgan4 = buggyRule $ rule "DeMorgan4" $ \x y -> Not (x :||: y) :~> Not x :||: Not y buggyRuleDeMorgan5 :: Rule SLogic-buggyRuleDeMorgan5 = buggyRule $ ruleList "BuggyDeMorgan5"+buggyRuleDeMorgan5 = buggyRule $ ruleList "DeMorgan5" [ \x y z -> Not (Not (x :&&: y) :||: z) :~> Not (Not x :||: Not y):||: z , \x y z -> Not (Not (x :&&: y) :&&: z) :~> Not (Not x :||: Not y):&&: z , \x y z -> Not (Not (x :||: y) :||: z) :~> Not (Not x :&&: Not y):||: z@@ -159,21 +167,21 @@ ] buggyRuleNotOverImpl :: Rule SLogic-buggyRuleNotOverImpl = buggyRule $ rule "BuggyNotOverImpl" $+buggyRuleNotOverImpl = buggyRule $ rule "NotOverImpl" $ \x y -> Not (x :->: y) :~> Not x :->: Not y buggyRuleParenth1 :: Rule SLogic-buggyRuleParenth1 = buggyRule $ ruleList "BuggyParenth1"+buggyRuleParenth1 = buggyRule $ ruleList "Parenth1" [ \x y -> Not (x :&&: y) :~> Not x :&&: y , \x y -> Not (x :||: y) :~> Not x :||: y ] buggyRuleParenth2 :: Rule SLogic-buggyRuleParenth2 = buggyRule $ rule "BuggyParenth2" $+buggyRuleParenth2 = buggyRule $ rule "Parenth2" $ \x y -> Not (x :<->: y) :~> Not(x :&&: y) :||: (Not x :&&: Not y) buggyRuleParenth3 :: Rule SLogic-buggyRuleParenth3 = buggyRule $ ruleList "BuggyParenth3" +buggyRuleParenth3 = buggyRule $ ruleList "Parenth3" [ \x y -> Not (Not x :&&: y) :~> x :&&: y , \x y -> Not (Not x :||: y) :~> x :||: y , \x y -> Not (Not x :->: y) :~> x :->: y@@ -182,7 +190,7 @@ buggyRuleAssoc :: Rule SLogic-buggyRuleAssoc = buggyRule $ ruleList "BuggyAssoc"+buggyRuleAssoc = buggyRule $ ruleList "Assoc" [ \x y z -> x :||: (y :&&: z) :~> (x :||: y) :&&: z , \x y z -> (x :||: y) :&&: z :~> x :||: (y :&&: z) , \x y z -> (x :&&: y) :||: z :~> x :&&: (y :||: z)@@ -190,7 +198,7 @@ ] buggyRuleAbsor :: Rule SLogic-buggyRuleAbsor = buggyRule $ ruleList "BuggyAbsor"+buggyRuleAbsor = buggyRule $ ruleList "Absor" [ \x y z -> (x :||: y) :||: ((x :&&: y) :&&: z) :~> (x :||: y) , \x y z -> (x :&&: y) :||: ((x :||: y) :&&: z) :~> (x :&&: y) , \x y z -> (x :||: y) :&&: ((x :&&: y) :||: z) :~> (x :||: y) @@ -198,7 +206,7 @@ ] buggyRuleDistr :: Rule SLogic-buggyRuleDistr = buggyRule $ ruleList "BuggyDistr"+buggyRuleDistr = buggyRule $ ruleList "Distr" [ \x y z -> x :&&: (y :||: z) :~> (x :&&: y) :&&: (x :&&: z) , \x y z -> (x :||: y) :&&: z :~> (x :&&: z) :&&: (y :&&: z) , \x y z -> x :&&: (y :||: z) :~> (x :||: y) :&&: (x :||: z)@@ -210,7 +218,7 @@ ] buggyRuleDistrNot :: Rule SLogic-buggyRuleDistrNot = buggyRule $ ruleList "BuggyDistrNot"+buggyRuleDistrNot = buggyRule $ ruleList "DistrNot" [ \x y z -> Not x :&&: (y :||: z) :~> (Not x :&&: y) :||: (x :&&: z) , \x y z -> Not x :&&: (y :||: z) :~> (x :&&: y) :||: (Not x :&&: z) , \x y z -> (x :||: y) :&&: Not z :~> (x :&&: Not z) :||: (y :&&: z)
+ src/Domain/Logic/Examples.hs view
@@ -0,0 +1,49 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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 : josje.lodder@ou.nl +-- Stability : provisional +-- Portability : portable (depends on ghc) +-- +-- A set of example proofs +-- +----------------------------------------------------------------------------- +module Domain.Logic.Examples + ( exampleProofs + ) where + + +import Domain.Logic.Formula +import Common.Utils (ShowString(..)) + + + + + +exampleProofs :: [(SLogic, SLogic)] +exampleProofs = [(Not(p :||: (Not p :&&: q)), Not(p :||: q)), + ((p :->:q):||: Not p, (p :->: q) :||: q), + ((p :&&: Not q):||:(q :&&: Not p), (p :||:q):&&:Not(p :&&: q)), + (Not(p :||: Not(p :||: Not q)), Not(p :||: q)), + (p :<->: q, (p :->: q) :&&: (q :->: p)), + ((p :&&: q) :->: p, T), + ((p :->: q) :||: (q :->: p), T), + ((q :->: (Not p :->: q)) :->: p, Not p :->: (q :&&: ((p :&&: q) :&&: q))), + ((p :->: Not q):->:q, (s :||:(s :->:(q :||: p))) :&&: q), + (p :->: (q :->: r), (p :->: q) :->: (p :->:r)), + (Not((p :->: q) :->: Not(q :->: p)), p :<->: q), + ((p :->: q):->: (p :->: s), (Not q :->: Not p) :->: (Not s :->: Not p)), + (Not((p :->:q) :->: (p:&&:q)), (p :->: q) :&&: (Not p :||: Not q)), + (Not((p :<->: q) :->: (p :||: (p :<->: q))), F)] + + where + p = Var (ShowString "p") + q = Var (ShowString "q") + s = Var (ShowString "s") + r = Var (ShowString "r") + + +
src/Domain/Logic/Exercises.hs view
@@ -34,8 +34,8 @@ -- Currently, we use the DWA strategy dnfExercise :: Exercise SLogic dnfExercise = makeExercise - { description = "Proposition to DNF" - , exerciseCode = makeCode "logic" "dnf" + { exerciseId = describe "Proposition to DNF" $ + newId "logic.propositional.dnf" , status = Stable , parser = parseLogicPars , prettyPrinter = ppLogicPars @@ -54,8 +54,8 @@ -- Direct support for unicode characters dnfUnicodeExercise :: Exercise SLogic dnfUnicodeExercise = dnfExercise - { description = description dnfExercise ++ " (unicode support)" - , exerciseCode = makeCode "logic" "dnf-unicode" + { exerciseId = describe "Proposition to DNF (unicode support)" $ + newId "logic.propositional.dnf.unicode" , parser = parseLogicUnicodePars , prettyPrinter = ppLogicUnicodePars } @@ -66,8 +66,8 @@ | n == 1 = generateLevel Easy | n == 3 = generateLevel Difficult | otherwise = generateLevel Normal - ok p = let n = fromMaybe maxBound (stepsRemaining maxStep p) - in countEquivalences p <= 2 && n >= minStep && n <= maxStep + ok p = let i = fromMaybe maxBound (stepsRemaining maxStep p) + in countEquivalences p <= 2 && i >= minStep && i <= maxStep in restrictGenerator ok gen suitable :: SLogic -> Bool
src/Domain/Logic/FeedbackText.hs view
@@ -16,13 +16,14 @@ import Data.List import Data.Maybe+import Common.Id import Common.Transformation import Domain.Logic.Rules import Domain.Logic.BuggyRules feedbackSyntaxError :: String -> String feedbackSyntaxError msg- | take 1 msg == "(" = "Syntax error at " ++ msg+ | "(" `isPrefixOf` msg = "Syntax error at " ++ msg | "Syntax error" `isPrefixOf` msg = msg | otherwise = "Syntax error: " ++ msg @@ -104,7 +105,7 @@ feedbackDetour :: Bool -> Maybe (Rule a) -> [Rule a] -> (String, Bool) feedbackDetour True _ [one] = (appliedRule one ++ " " ++ feedbackFinished, True) feedbackDetour True _ _ = (feedbackMultipleSteps ++ " " ++ feedbackFinished, True)-feedbackDetour _ _ [one] | one `inGroup`"Commutativity" =+feedbackDetour _ _ [one] | one `inGroup` groupCommutativity = ("You have applied one of the commutativity rules correctly. This step is not mandatory, but sometimes helps to simplify the formula.", True) feedbackDetour _ mexp [one] = let however = case mexp >>= ruleText of@@ -134,15 +135,15 @@ | r ~= ruleNotNot = return "double negation" | r ~= ruleDefImpl = return "implication elimination" | r ~= ruleDefEquiv = return "equivalence elimination" - | r `inGroup`"Commutativity" = return "commutativity"- | r `inGroup`"Aasociativity" = return "associativity"- | r `inGroup`"DistributionOrOverAnd" = return "distribution of or over and"- | r `inGroup`"DistributionAndOverOr" = return "distribution of and over or"- | r `inGroup`"Idempotency" = return "idempotency"- | r `inGroup`"Absorption" = return "absorption"- | r `inGroup`"De Morgan" = return "De Morgan"- | r `inGroup`"InverseDeMorgan" = return "De Morgan"- | r `inGroup`"InverseDistr" = return "distributivity"+ | r `inGroup` groupCommutativity = return "commutativity"+ | r `inGroup` groupAssociativity = return "associativity"+ | r `inGroup` groupDistributionOrOverAnd = return "distribution of or over and"+ | r `inGroup` groupDistributionAndOverOr = return "distribution of and over or"+ | r `inGroup` groupIdempotency = return "idempotency"+ | r `inGroup` groupAbsorption = return "absorption"+ | r `inGroup` groupDeMorgan = return "De Morgan"+ | r `inGroup` groupInverseDeMorgan = return "De Morgan"+ | r `inGroup` groupInverseDistr = return "distributivity" -- TODO Josje: aanvullen met alle regels (ook die ook in de DWA strategie voorkomen) | otherwise = Nothing -------------------------------------------------------------------------@@ -162,10 +163,10 @@ -- Helper functions (~=) :: Rule a -> Rule b -> Bool-r1 ~= r2 = name r1 == name r2+r1 ~= r2 = getId r1 == getId r2 -- Quick and dirty fix!-inGroup :: Rule a -> String -> Bool+inGroup :: Rule a -> (Id, b) -> Bool inGroup r n = let rs = filter (~= r) (logicRules ++ buggyRules)- in n `elem` concatMap ruleGroups rs+ in fst n `elem` concatMap ruleGroups rs
src/Domain/Logic/Formula.hs view
@@ -11,14 +11,17 @@ ----------------------------------------------------------------------------- module Domain.Logic.Formula where -import Domain.Math.Expr.Symbolic-import Text.OpenMath.Dictionary.Logic1-import Common.Uniplate (Uniplate(..), universe)+import Common.Classes+import Common.Id import Common.Rewriting-import Common.Traversable+import Common.Uniplate (Uniplate(..), universe) import Common.Utils (ShowString, subsets)-import Data.List+import Common.View import Control.Monad+import Data.List+import Data.Maybe+import Domain.Math.Expr.Symbols (openMathSymbol)+import qualified Text.OpenMath.Dictionary.Logic1 as OM infixr 2 :<->: infixr 3 :->: @@ -61,16 +64,16 @@ -- | foldLogic is the standard fold for Logic. foldLogic :: LogicAlg b a -> Logic b -> a-foldLogic (var, impl, equiv, and, or, not, true, false) = rec+foldLogic (var, impl, equiv, conj, disj, neg, true, false) = rec where rec logic = case logic of Var x -> var x p :->: q -> rec p `impl` rec q p :<->: q -> rec p `equiv` rec q- p :&&: q -> rec p `and` rec q- p :||: q -> rec p `or` rec q- Not p -> not (rec p)+ p :&&: q -> rec p `conj` rec q+ p :||: q -> rec p `disj` rec q+ Not p -> neg (rec p) T -> true F -> false @@ -79,8 +82,10 @@ ppLogic = ppLogicPrio 0 ppLogicPrio :: Show a => Int -> Logic a -> String-ppLogicPrio n p = foldLogic (pp . show, binop 3 "->", binop 0 "<->", binop 2 "/\\", binop 1 "||", nott, pp "T", pp "F") p n ""+ppLogicPrio = (\f s -> f s "") . flip (foldLogic alg) where+ alg = ( pp . show, binop 3 "->", binop 0 "<->", binop 2 "/\\"+ , binop 1 "||", nott, pp "T", pp "F") binop prio op p q n = parIf (n > prio) (p (prio+1) . ((" "++op++" ")++) . q prio) pp s = const (s++) nott p _ = ("~"++) . p 4@@ -105,13 +110,6 @@ xs = varsLogic p `union` varsLogic q fs = map (flip elem) (subsets xs) --- | Functions noNot, noOr, and noAnd determine whether or not a Logic --- | expression contains a not, or, and and constructor, respectively.-noNot, noOr, noAnd :: Logic a -> Bool-noNot = foldLogic (const True, (&&), (&&), (&&), (&&), const False, True, True)-noOr = foldLogic (const True, (&&), (&&), (&&), \_ _ -> False, id, True, True)-noAnd = foldLogic (const True, (&&), (&&), \_ _ -> False, (&&), id, True, True)- -- | A Logic expression is atomic if it is a variable or a constant True or False. isAtomic :: Logic a -> Bool isAtomic logic = @@ -131,56 +129,30 @@ -- | Function disjunctions returns all Logic expressions separated by an or -- | operator at the top level. disjunctions :: Logic a -> [Logic a]-disjunctions = collectWithOperator orOperator+disjunctions p = fromMaybe [p] $ match (magmaListView orMonoid) p -- | Function conjunctions returns all Logic expressions separated by an and -- | operator at the top level. conjunctions :: Logic a -> [Logic a]-conjunctions = collectWithOperator andOperator---- | Count the number of implicationsations :: Logic -> Int-countImplications :: Logic a -> Int-countImplications p = length [ () | _ :->: _ <- universe p ] +conjunctions p = fromMaybe [p] $ match (magmaListView andMonoid) p -- | Count the number of equivalences countEquivalences :: Logic a -> Int countEquivalences p = length [ () | _ :<->: _ <- universe p ] --- | Count the number of binary operators-countBinaryOperators :: Logic a -> Int-countBinaryOperators = foldLogic (const 0, binop, binop, binop, binop, id, 0, 0)- where binop x y = x + y + 1---- | Count the number of double negations -countDoubleNegations :: Logic a -> Int-countDoubleNegations p = length [ () | Not (Not _) <- universe p ] - -- | Function varsLogic returns the variables that appear in a Logic expression. varsLogic :: Eq a => Logic a -> [a] varsLogic p = nub [ s | Var s <- universe p ] instance Uniplate (Logic a) where- uniplate p =- case p of + uniplate this =+ case this of p :->: q -> ([p, q], \[a, b] -> a :->: b) p :<->: q -> ([p, q], \[a, b] -> a :<->: b) p :&&: q -> ([p, q], \[a, b] -> a :&&: b) p :||: q -> ([p, q], \[a, b] -> a :||: b) Not p -> ([p], \[a] -> Not a)- _ -> ([], \[] -> p)--instance Eq a => ShallowEq (Logic a) where- shallowEq expr1 expr2 =- case (expr1, expr2) of- (Var a, Var b) -> a==b- (_ :->: _ , _ :->: _ ) -> True- (_ :<->: _, _ :<->: _) -> True- (_ :&&: _ , _ :&&: _ ) -> True- (_ :||: _ , _ :||: _ ) -> True- (Not _ , Not _ ) -> True- (T , T ) -> True- (F , F ) -> True- _ -> False+ _ -> ([], \[] -> this) instance Different (Logic a) where different = (T, F)@@ -189,7 +161,7 @@ toTerm = foldLogic ( toTerm, binary impliesSymbol, binary equivalentSymbol , binary andSymbol, binary orSymbol, unary notSymbol- , nullary trueSymbol, nullary falseSymbol+ , symbol trueSymbol, symbol falseSymbol ) fromTerm a = @@ -203,23 +175,57 @@ f s [x, y] | s == impliesSymbol = return (x :->: y) | s == equivalentSymbol = return (x :<->: y)- | s == andSymbol = return (x :&&: y)- | s == orSymbol = return (x :||: y)+ f s xs@(_:_)+ | s == andSymbol = return (foldr1 (:&&:) xs)+ | s == orSymbol = return (foldr1 (:||:) xs) f _ _ = fail "fromTerm" -logicOperators :: Operators (Logic a)-logicOperators = [andOperator, orOperator]+trueSymbol, falseSymbol, notSymbol, impliesSymbol, equivalentSymbol,+ andSymbol, orSymbol :: Symbol++trueSymbol = openMathSymbol OM.trueSymbol+falseSymbol = openMathSymbol OM.falseSymbol+notSymbol = openMathSymbol OM.notSymbol+impliesSymbol = openMathSymbol OM.impliesSymbol+equivalentSymbol = openMathSymbol OM.equivalentSymbol+andSymbol = openMathSymbol OM.andSymbol+orSymbol = openMathSymbol OM.orSymbol++logicOperators :: [Magma (Logic a)]+logicOperators = map toMagma [andMonoid, orMonoid]++andMonoid :: Monoid (Logic a)+andMonoid = monoid andOperator (makeConstant (getId trueSymbol) T isT)+ where+ isT T = True+ isT _ = False --- The "and" operator is also commutative, but not (yet) in the equational theory-andOperator :: Operator (Logic a)-andOperator = associativeOperator (:&&:) isAnd+orMonoid :: Monoid (Logic a)+orMonoid = monoid orOperator (makeConstant (getId falseSymbol) F isF) where+ isF F = True+ isF _ = False++andOperator:: BinaryOp (Logic a)+andOperator = makeBinary (getId andSymbol) (:&&:) isAnd+ where isAnd (p :&&: q) = Just (p, q) isAnd _ = Nothing --- The "or" operator is also commutative, but not (yet) in the equational theory-orOperator :: Operator (Logic a)-orOperator = associativeOperator (:||:) isOr+orOperator :: BinaryOp (Logic a)+orOperator = makeBinary (getId orSymbol) (:||:) isOr where isOr (p :||: q) = Just (p, q) isOr _ = Nothing++implOperator :: BinaryOp (Logic a) +implOperator = makeBinary (getId impliesSymbol) (:->:) isImpl+ where+ isImpl (p :->: q) = Just (p, q)+ isImpl _ = Nothing+ +equivOperator :: BinaryOp (Logic a) +equivOperator = makeBinary (getId equivalentSymbol) (:<->:) isEquiv+ where+ isEquiv (p :<->: q) = Just (p, q)+ isEquiv _ = Nothing
src/Domain/Logic/GeneralizedRules.hs view
@@ -22,7 +22,8 @@ -- Note: the generalized rules do not take AC-unification into account, -- and perhaps they should. import Domain.Logic.Formula-import Common.Transformation+import Common.Transformation (Rule)+import qualified Common.Transformation as Rule import Control.Monad generalRules :: [Rule SLogic]@@ -36,6 +37,9 @@ [ inverseDeMorganOr, inverseDeMorganAnd , inverseAndOverOr, inverseOrOverAnd ]++makeSimpleRule :: String -> (a -> Maybe a) -> Rule a+makeSimpleRule s = Rule.makeSimpleRule ("logic.propositional." ++ s) ----------------------------------------------------------------------------- -- Inverse rules
src/Domain/Logic/Generator.hs view
@@ -21,20 +21,24 @@ import Test.QuickCheck import Common.Rewriting import Common.Uniplate -import Domain.Math.Expr.Symbolic -import Text.OpenMath.Dictionary.Logic1 +import Common.View ------------------------------------------------------------- -- Code that doesn't belong here, but the arbitrary instance -- is needed for the Rewrite instance. instance Rewrite SLogic where - operators = logicOperators - associativeOps = const $ map toSymbol [andSymbol, orSymbol] + operators = logicOperators -- | Equality modulo associativity of operators equalLogicA:: SLogic -> SLogic -> Bool -equalLogicA = equalWith operators +equalLogicA p q = rec p == rec q + where + make = simplifyWith (map rec) . magmaListView + rec a = case a of + _ :&&: _ -> make andMonoid a + _ :||: _ -> make orMonoid a + _ -> descend rec a ----------------------------------------------------------- -- Logic generator @@ -61,18 +65,16 @@ -- Use the propositions with 4-12 steps normalGenerator :: Gen SLogic normalGenerator = do - n <- return 4 -- oneof [return 4, return 8] - p0 <- sizedGen False varGen n + p0 <- sizedGen False varGen 4 p1 <- preventSameVar varList p0 return (removePartsInDNF p1) -- Use the propositions with 7-18 steps difficultGenerator :: Gen SLogic difficultGenerator = do - let vars = ShowString "s" : varList - n <- return 4 -- oneof [return 4, return 8] - p0 <- sizedGen False (oneof $ map return vars) n - p1 <- preventSameVar vars p0 + let vs = ShowString "s" : varList + p0 <- sizedGen False (oneof $ map return vs) 4 + p1 <- preventSameVar vs p0 return (removePartsInDNF p1) varList :: [ShowString] @@ -104,12 +106,13 @@ -- Simple tricks for creating for "nice" logic propositions preventSameVar :: Eq a => [a] -> Logic a -> Gen (Logic a) -preventSameVar xs = transformM $ \p -> - case uniplate p of - ([Var a, Var b], f) | a==b -> do - c <- oneof $ map return $ filter (/=a) xs - return $ f [Var a, Var c] - _ -> return p +preventSameVar xs = rec + where + rec p = case holes p of + [(Var a, _), (Var b, update)] | a==b -> do + c <- oneof $ map return $ filter (/=a) xs + return $ update (Var c) + _ -> descendM rec p removePartsInDNF :: SLogic -> SLogic removePartsInDNF = buildOr . filter (not . simple) . disjunctions @@ -127,14 +130,15 @@ instance Arbitrary SLogic where arbitrary = sized (\i -> sizedGen True varGen (i `min` 4)) + instance CoArbitrary SLogic where coarbitrary logic = case logic of - Var x -> variant 0 . coarbitrary (map ord (fromShowString x)) - p :->: q -> variant 1 . coarbitrary p . coarbitrary q - p :<->: q -> variant 2 . coarbitrary p . coarbitrary q - p :&&: q -> variant 3 . coarbitrary p . coarbitrary q - p :||: q -> variant 4 . coarbitrary p . coarbitrary q - Not p -> variant 5 . coarbitrary p - T -> variant 6 - F -> variant 7+ Var x -> variant (0 :: Int) . coarbitrary (map ord (fromShowString x)) + p :->: q -> variant (1 :: Int) . coarbitrary p . coarbitrary q + p :<->: q -> variant (2 :: Int) . coarbitrary p . coarbitrary q + p :&&: q -> variant (3 :: Int) . coarbitrary p . coarbitrary q + p :||: q -> variant (4 :: Int) . coarbitrary p . coarbitrary q + Not p -> variant (5 :: Int) . coarbitrary p + T -> variant (6 :: Int) + F -> variant (7 :: Int)
src/Domain/Logic/Parser.hs view
@@ -10,11 +10,12 @@ -- ----------------------------------------------------------------------------- module Domain.Logic.Parser - ( parseLogic, parseLogicPars, parseLogicUnicodePars + ( parseLogic, parseLogicPars, parseLogicUnicodePars, parseLogicProof , ppLogicPars, ppLogicUnicodePars ) where import Common.Utils (ShowString(..)) +import Control.Monad.Error (liftM2) import Text.Parsing import Control.Arrow import Domain.Logic.Formula @@ -75,18 +76,20 @@ | [c] == equivUSym = equivASym | otherwise = [c] -pLogicGen (impl, equiv, and, or, nt, tr, fl) = pLogic +pLogicGen :: SymbolTuple -> TokenParser SLogic +pLogicGen (impl, equiv, conj, disj, neg, tr, fl) = pLogic where pLogic = flip ($) <$> basic <*> optional composed id - basic = basicWithPosGen (nt, tr, fl) pLogic + basic = basicWithPosGen (neg, tr, fl) pLogic composed = flip (:<->:) <$ pKey equiv <*> basic <|> flip (:->:) <$ pKey impl <*> basic - <|> (\xs p -> foldr1 (:&&:) (p:xs)) <$> pList1 (pKey and *> basic) - <|> (\xs p -> foldr1 (:||:) (p:xs)) <$> pList1 (pKey or *> basic) + <|> (\xs p -> foldr1 (:&&:) (p:xs)) <$> pList1 (pKey conj *> basic) + <|> (\xs p -> foldr1 (:||:) (p:xs)) <$> pList1 (pKey disj *> basic) -basicWithPos :: Parser Token SLogic -> Parser Token SLogic +basicWithPos :: TokenParser SLogic -> TokenParser SLogic basicWithPos = basicWithPosGen ("~", "T", "F") +basicWithPosGen :: (String, String, String) -> TokenParser SLogic -> TokenParser SLogic basicWithPosGen t@(nt, tr, fl) p = (Var . ShowString) <$> pVarid <|> pParens p @@ -94,11 +97,23 @@ <|> F <$ pKey fl <|> Not <$ pKey nt <*> basicWithPosGen t p +parseLogicProof :: String -> Either String (SLogic, SLogic) +parseLogicProof s + = either Left susp + $ left (ambiguousOperators parseLogic s) + $ analyseAndParse pProof + $ scanWith extScanner s + where + pProof = (,) <$> pLogicGen asciiTuple <* pKey "==" <*> pLogicGen asciiTuple + susp (p, q) = liftM2 (,) (suspiciousVariable p) (suspiciousVariable q) + extScanner = logicScanner + {keywordOperators = "==" : keywordOperators logicScanner} + ----------------------------------------------------------- --- Helper-functions for syntax warnings -- analyze parentheses -analyseAndParse :: Parser Token a -> [Token] -> Either String a +analyseAndParse :: TokenParser a -> [Token] -> Either String a analyseAndParse p ts = case checkParentheses ts of Just err -> Left (show err) @@ -133,21 +148,27 @@ ppLogicUnicodePars :: SLogic -> String ppLogicUnicodePars = ppLogicParsGen unicodeTuple -ppLogicParsGen (impl, equiv, and, or, nt, tr, fl) p = foldLogic alg p 0 "" +ppLogicParsGen :: SymbolTuple -> SLogic -> String +ppLogicParsGen (impl, equiv, conj, disj, neg, tr, fl) = + (\f -> f 0 "") . foldLogic alg where - alg = (pp . fromShowString, binop 3 impl, binop 3 equiv, binop 1 and, binop 2 or, nott, pp tr, pp fl) - binop prio op p q n = parIf (n/=0 && (n==3 || prio/=n)) - (p prio . ((" "++op++" ")++) . q prio) + alg = ( pp . fromShowString, binop 3 impl, binop 3 equiv, binop 1 conj + , binop 2 disj, nott, pp tr, pp fl + ) + binop :: Int -> String -> (Int -> String -> String) -> (Int -> String -> String) -> Int -> String -> String + binop prio op p q n = + parIf (n/=0 && (n==3 || prio/=n)) + (p prio . ((" "++op++" ")++) . q prio) pp s = const (s++) - nott p _ = (nt++) . p 3 + nott p _ = (neg++) . p 3 parIf b f = if b then ("("++) . f . (")"++) else f ----------------------------------------------------------- --- Ascii symbols ---asciiSyms :: [String] ---asciiSyms = [implASym, equivASym, andASym, orASym, notASym] +type SymbolTuple = (String, String, String, String, String, String, String) +asciiTuple :: SymbolTuple asciiTuple = (implASym, equivASym, andASym, orASym, notASym, "T", "F") implASym, equivASym, andASym, orASym, notASym :: String @@ -163,6 +184,7 @@ unicodeSyms :: [String] unicodeSyms = [implUSym, equivUSym, andUSym, orUSym, notUSym] +unicodeTuple :: SymbolTuple unicodeTuple = (implUSym, equivUSym, andUSym, orUSym, notUSym, "T", "F") implUSym, equivUSym, andUSym, orUSym, notUSym :: String
+ src/Domain/Logic/Proofs.hs view
@@ -0,0 +1,319 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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) +-- +-- Exercise for the logic domain: to prove two propositions equivalent +-- +----------------------------------------------------------------------------- +module Domain.Logic.Proofs (proofExercise) where + +import Prelude hiding (repeat) +import Common.Context +import Common.Rewriting +import Common.Rewriting.AC +import Common.Strategy hiding (fail, not) +import Common.Exercise +import Common.Utils +import Common.View +import Common.Transformation +import Common.Navigator +import Data.List hiding (repeat) +import Control.Monad +import Data.Maybe +import Domain.Logic.Formula +import Domain.Logic.Generator (equalLogicA) +import Domain.Logic.Parser +import Domain.Logic.Rules +import Domain.Logic.GeneralizedRules +import Domain.Logic.Strategies (somewhereOr) +import Domain.Logic.Examples +import Domain.Math.Expr () +import Common.Uniplate + +see :: Int -> IO () +see n = printDerivation proofExercise (examples proofExercise !! n) + +-- Currently, we use the DWA strategy +proofExercise :: Exercise [(SLogic, SLogic)] +proofExercise = makeExercise + { exerciseId = describe "Prove two propositions equivalent" $ + newId "logic.proof" + , status = Experimental +-- , parser = parseLogicProof + , prettyPrinter = let f (p, q) = ppLogicPars p ++ " == " ++ ppLogicPars q + in commaList . map f +-- , equivalence = \(p, _) (r, s) -> eqLogic p r && eqLogic r s +-- , similarity = \(p, q) (r, s) -> equalLogicA p r && equalLogicA q s + , isSuitable = all (uncurry eqLogic) + , isReady = all (uncurry equalLogicA) + , strategy = proofStrategy + , navigation = termNavigator + , examples = map return $ exampleProofs ++ + let p = Var (ShowString "p") + q = Var (ShowString "q") + in [(q :&&: p, p :&&: (q :||: q))] + } + +instance (IsTerm a, IsTerm b) => IsTerm (a, b) where + toTerm (a, b) = binary tupleSymbol (toTerm a) (toTerm b) + fromTerm term = do + (a, b) <- isBinary tupleSymbol term + liftM2 (,) (fromTerm a) (fromTerm b) + +tupleSymbol :: Symbol +tupleSymbol = newSymbol "basic.tuple" + +proofStrategy :: LabeledStrategy (Context [(SLogic, SLogic)]) +proofStrategy = label "proof equivalent" $ + repeat ( + somewhere (useC commonExprAtom) + |> somewhere splitTop + |> somewhere rest + ) <*> + repeat (somewhere (use normLogicRule)) + where + splitTop = use topIsNot <|> use topIsAnd <|> use topIsOr + <|> use topIsImpl <|> use topIsEquiv + rest = use notDNF <*> mapRulesS useC (repeat dnfStrategyDWA) + <|> simpler + + simpler :: Strategy (Context [(SLogic, SLogic)]) + simpler = + use tautologyOr <|> use idempotencyAnd <|> use contradictionAnd + <|> use absorptionSubset <|> use fakeAbsorption <|> use fakeAbsorptionNot + <|> alternatives (map use list) + + list = [ ruleFalseZeroOr, ruleTrueZeroOr, ruleIdempOr + , ruleAbsorpOr, ruleComplOr + ] + + notDNF :: Rule SLogic + notDNF = minorRule $ makeSimpleRule "not-dnf" $ \p -> + if isDNF p then Nothing else Just p + +----------------------------------------------------------------------------- +-- To DNF, with priorities (the "DWA" approach) + +dnfStrategyDWA :: Strategy (Context SLogic) +dnfStrategyDWA = + toplevel <|> somewhereOr + ( label "Simplify" simplify + |> label "Eliminate implications/equivalences" eliminateImplEquiv + |> label "Eliminate nots" eliminateNots + |> label "Move ors to top" orToTop + ) + where + toplevel = useRules + [ ruleFalseZeroOr, ruleTrueZeroOr, ruleIdempOr + , ruleAbsorpOr, ruleComplOr + ] + simplify = somewhere $ useRules + [ ruleFalseZeroOr, ruleTrueZeroOr, ruleTrueZeroAnd + , ruleFalseZeroAnd, ruleNotTrue, ruleNotFalse + , ruleNotNot, ruleIdempOr, ruleIdempAnd, ruleAbsorpOr, ruleAbsorpAnd + , ruleComplOr, ruleComplAnd + ] + eliminateImplEquiv = somewhere $ useRules + [ ruleDefImpl, ruleDefEquiv + ] + eliminateNots = somewhere $ useRules + [ generalRuleDeMorganAnd, generalRuleDeMorganOr + , ruleDeMorganAnd, ruleDeMorganOr + ] + orToTop = somewhere $ useRules + [ generalRuleAndOverOr, ruleAndOverOr ] + +useRules :: [Rule SLogic] -> Strategy (Context SLogic) +useRules = alternatives . map liftToContext + +onceLeft :: IsStrategy f => f (Context a) -> Strategy (Context a) +onceLeft s = ruleMoveDown <*> s <*> ruleMoveUp + where + ruleMoveDown = minorRule $ makeSimpleRuleList "MoveDown" (down 1) + ruleMoveUp = minorRule $ makeSimpleRule "MoveUp" safeUp + + safeUp a = Just (fromMaybe a (up a)) + +onceRight :: IsStrategy f => f (Context a) -> Strategy (Context a) +onceRight s = ruleMoveDown <*> s <*> ruleMoveUp + where + ruleMoveDown = minorRule $ makeSimpleRuleList "MoveDown" (down 2) + ruleMoveUp = minorRule $ makeSimpleRule "MoveUp" safeUp + + safeUp a = Just (fromMaybe a (up a)) + +testje :: Rule (Context SLogic) +testje = makeSimpleRule "testje" $ \a -> error $ show a + +go n = printDerivation proofExercise [exampleProofs !! n] --(p :||: Not p, Not F) + --where p = Var (ShowString "p") + +normLogicRule :: Rule (SLogic, SLogic) +normLogicRule = makeSimpleRule "Normalize" $ \tuple@(p, q) -> do + guard (p /= q) + let xs = sort (varsLogic p `union` varsLogic q) + new = (normLogicWith xs p, normLogicWith xs q) + guard (tuple /= new) + return new + +-- Find a common subexpression that can be treated as a box +commonExprAtom :: Rule (Context (SLogic, SLogic)) +commonExprAtom = makeSimpleRule "commonExprAtom" $ withCM $ \(p, q) -> do + let f = filter same . filter ok . nub . sort . universe + xs = f p `intersect` f q -- todo: only largest common sub expr + ok (Var _) = False + ok T = False + ok F = False + ok (Not a) = ok a + ok _ = True + same cse = eqLogic (sub cse p) (sub cse q) + new = head (logicVars \\ (varsLogic p `union` varsLogic q)) + sub a this + | a == this = Var new + | otherwise = descend (sub a) this + case xs of + hd:_ -> do modifyVar substVar ((show new, show hd):) + return (sub hd p, sub hd q) + _ -> fail "not applicable" + +substVar :: Var [(String, String)] +substVar = newVar "subst" [] + +logicVars :: [ShowString] +logicVars = [ ShowString [c] | c <- ['a'..] ] + +normLogic :: Ord a => Logic a -> Logic a +normLogic p = normLogicWith (sort (varsLogic p)) p + +normLogicWith :: Eq a => [a] -> Logic a -> Logic a +normLogicWith xs p = make (filter keep (subsets xs)) + where + keep ys = evalLogic (`elem` ys) p + make = makeOrs . map atoms + atoms ys = makeAnds [ f (x `elem` ys) (Var x) | x <- xs ] + f b = if b then id else Not + +makeOrs xs = if null xs then F else foldr1 (:||:) xs +makeAnds xs = if null xs then T else foldr1 (:&&:) xs + + +-- p \/ q \/ ~p ~> T (propageren) +tautologyOr :: Rule SLogic +tautologyOr = makeSimpleRule "tautologyOr" $ \p -> do + let xs = disjunctions p + guard (any (\x -> Not x `elem` xs) xs) + return T + +-- p /\ q /\ p ~> p /\ q +idempotencyAnd :: Rule SLogic +idempotencyAnd = makeSimpleRule "idempotencyAnd" $ \p -> do + let xs = conjunctions p + ys = nub xs + guard (length ys < length xs) + return (makeAnds ys) + +-- p /\ q /\ ~p ~> F (propageren) +contradictionAnd :: Rule SLogic +contradictionAnd = makeSimpleRule "contradictionAnd" $ \p -> do + let xs = conjunctions p + guard (any (\x -> Not x `elem` xs) xs) + return F + +-- (p /\ q) \/ ... \/ (p /\ q /\ r) ~> (p /\ q) \/ ... +-- (subset relatie tussen rijtjes: bijzonder geval is gelijke rijtjes) +absorptionSubset :: Rule SLogic +absorptionSubset = makeSimpleRule "absorptionSubset" $ \p -> do + let xss = map conjunctions (disjunctions p) + yss = nub $ filter (\xs -> all (ok xs) xss) xss + ok xs ys = not (ys `isSubsetOf` xs) || xs == ys + guard (length yss < length xss) + return $ makeOrs (map makeAnds yss) + +-- p \/ ... \/ (~p /\ q /\ r) ~> p \/ ... \/ (q /\ r) +-- (p is hier een losse variabele) +fakeAbsorption :: Rule SLogic +fakeAbsorption = makeSimpleRuleList "fakeAbsorption" $ \p -> do + let xs = disjunctions p + v <- [ a | a@(Var _) <- xs ] + let ys = map (makeAnds . filter (/= Not v) . conjunctions) xs + new = makeOrs ys + guard (p /= new) + return new + +-- ~p \/ ... \/ (p /\ q /\ r) ~> ~p \/ ... \/ (q /\ r) +-- (p is hier een losse variabele) +fakeAbsorptionNot :: Rule SLogic +fakeAbsorptionNot = makeSimpleRuleList "fakeAbsorptionNot" $ \p -> do + let xs = disjunctions p + v <- [ a | Not a@(Var _) <- xs ] + let ys = map (makeAnds . filter (/= v) . conjunctions) xs + new = makeOrs ys + guard (p /= new) + return new + +topIsNot :: Rule (SLogic, SLogic) +topIsNot = makeSimpleRule "top-is-not" f + where + f (Not p, Not q) = Just (p, q) + f _ = Nothing + +acTopRuleFor :: IsId a => a -> BinaryOp SLogic -> Rule [(SLogic, SLogic)] +acTopRuleFor s op = makeSimpleRuleList s f + where + f [(lhs, rhs)] = do + let myView = magmaListView (semiGroup op) + make = build myView + xs <- matchM myView lhs + ys <- matchM myView rhs + guard (length xs > 1 && length ys > 1) + list <- liftM (map (make *** make)) (pairingsAC False xs ys) + guard (all (uncurry eqLogic) list) + return list + f _ = [] + +topIsAnd :: Rule [(SLogic, SLogic)] +topIsAnd = acTopRuleFor "top-is-and" andOperator + +topIsOr :: Rule [(SLogic, SLogic)] +topIsOr = acTopRuleFor "top-is-or" orOperator + +topIsEquiv :: Rule [(SLogic, SLogic)] +topIsEquiv = acTopRuleFor "top-is-equiv" equivOperator + +topIsImpl :: Rule [(SLogic, SLogic)] +topIsImpl = makeSimpleRule "top-is-impl" f + where + f [(p :->: q, r :->: s)] = do + guard (eqLogic p r && eqLogic q s) + return [(p, r), (q, s)] + f _ = Nothing + +{- Strategie voor sterke(?) normalisatie + +(prioritering) + +1. p \/ q \/ ~p ~> T (propageren) + p /\ q /\ p ~> p /\ q + p /\ q /\ ~p ~> F (propageren) + +2. (p /\ q) \/ ... \/ (p /\ q /\ r) ~> (p /\ q) \/ ... + (subset relatie tussen rijtjes: bijzonder geval is gelijke rijtjes) + p \/ ... \/ (~p /\ q /\ r) ~> p \/ ... \/ (q /\ r) + (p is hier een losse variabele) + ~p \/ ... \/ (p /\ q /\ r) ~> ~p \/ ... \/ (q /\ r) + (p is hier een losse variabele) + +3. a) elimineren wat aan een kant helemaal niet voorkomt (zie regel hieronder) + b) rijtjes sorteren + c) rijtjes aanvullen + +Twijfelachtige regel bij stap 3: samennemen in plaats van aanvullen: + (p /\ q /\ r) \/ ... \/ (~p /\ q /\ r) ~> q /\ r + (p is hier een losse variable) +-}
src/Domain/Logic/Rules.hs view
@@ -15,13 +15,15 @@ module Domain.Logic.Rules where import Domain.Logic.Formula -import Common.Transformation +import Common.Id +import Common.Transformation (Rule, addRuleToGroup, minorRule) import Common.Rewriting import Domain.Logic.Generator() import Domain.Logic.GeneralizedRules +import qualified Common.Transformation as Rule logicRules :: [Rule SLogic] -logicRules = concat +logicRules = concatMap snd [ groupCommutativity, groupAssociativity, groupIdempotency , groupAbsorption, groupTrueProperties, groupFalseProperties, groupDoubleNegation , groupDeMorgan, groupImplicationEliminatinon, groupEquivalenceElimination, groupAdditional @@ -29,21 +31,33 @@ , groupInverseDeMorgan,groupInverseDistr ] +logic :: IsId a => a -> Id +logic = ( # ) "logic.propositional" + +rule :: (RuleBuilder f a, Rewrite a) => String -> f -> Rule a +rule = Rule.rule . logic + +ruleList :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +ruleList = Rule.ruleList . logic + ----------------------------------------------------------------------------- -- Grouping DWA rules -makeGroup :: String -> [Rule SLogic] -> [Rule SLogic] -makeGroup = map . addRuleToGroup +makeGroup :: String -> [Rule SLogic] -> (Id, [Rule SLogic]) +makeGroup s rs = + let a = logic s + in (a, map (addRuleToGroup a) rs) -groupCommutativity, groupAssociativity, groupDistributionOrOverAnd, groupDistributionAndOverOr,groupIdempotency, - groupAbsorption, groupTrueProperties, groupFalseProperties, groupDoubleNegation, - groupDeMorgan, groupImplicationEliminatinon, groupEquivalenceElimination :: [Rule SLogic] +groupCommutativity, groupAssociativity, groupDistributionOrOverAnd, + groupDistributionAndOverOr,groupIdempotency, groupAbsorption, + groupTrueProperties, groupFalseProperties, groupDoubleNegation, + groupDeMorgan, groupImplicationEliminatinon, groupEquivalenceElimination, + groupInverseDeMorgan, groupInverseDistr :: (Id, [Rule SLogic]) groupCommutativity = makeGroup "Commutativity" [ruleCommOr, ruleCommAnd] groupAssociativity = makeGroup "Associativity" [ruleAssocOr, ruleAssocAnd] - groupIdempotency = makeGroup "Idempotency" [ruleIdempOr, ruleIdempAnd] groupAbsorption = makeGroup "Absorption" @@ -65,9 +79,9 @@ groupDistributionAndOverOr = makeGroup "DistributionAndOverOr" [generalRuleAndOverOr, ruleAndOverOr ] groupInverseDeMorgan = makeGroup "InverseDeMorgan" - [ inverseDeMorganOr, inverseDeMorganAnd] + [inverseDeMorganOr, inverseDeMorganAnd] groupInverseDistr = makeGroup "InverseDistr" - [ inverseAndOverOr, inverseOrOverAnd] + [inverseAndOverOr, inverseOrOverAnd] ----------------------------------------------------------------------------- -- Commutativity @@ -218,7 +232,7 @@ ----------------------------------------------------------------------------- -- Additional rules, not in the DWA course -groupAdditional :: [Rule SLogic] +groupAdditional :: (Id, [Rule SLogic]) groupAdditional = makeGroup "Additional rules" [ ruleFalseInEquiv, ruleTrueInEquiv, ruleFalseInImpl, ruleTrueInImpl , ruleCommEquiv, ruleDefEquivImpls, ruleEquivSame, ruleImplSame
src/Domain/Logic/Strategies.hs view
@@ -1,101 +1,103 @@------------------------------------------------------------------------------ --- Copyright 2010, 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 Domain.Logic.Strategies - ( dnfStrategy, dnfStrategyDWA) where - -import Prelude hiding (repeat) -import Domain.Logic.Rules -import Domain.Logic.GeneralizedRules -import Domain.Logic.Formula -import Common.Context (Context, liftToContext) -import Common.Rewriting (isOperator) -import Common.Transformation -import Common.Strategy -import Common.Navigator - ------------------------------------------------------------------------------ --- To DNF, with priorities (the "DWA" approachs) - -dnfStrategyDWA :: LabeledStrategy (Context SLogic) -dnfStrategyDWA = label "Bring to dnf (DWA)" $ - repeat $ toplevel <|> somewhereOr - ( label "Simplify" simplify - |> label "Eliminate implications/equivalences" eliminateImplEquiv - |> label "Eliminate nots" eliminateNots - |> label "Move ors to top" orToTop - ) - where - toplevel = useRules - [ ruleFalseZeroOr, ruleTrueZeroOr, ruleIdempOr - , ruleAbsorpOr, ruleComplOr - ] - simplify = somewhere $ useRules - [ ruleFalseZeroOr, ruleTrueZeroOr, ruleTrueZeroAnd - , ruleFalseZeroAnd, ruleNotTrue, ruleNotFalse - , ruleNotNot, ruleIdempOr, ruleIdempAnd, ruleAbsorpOr, ruleAbsorpAnd - , ruleComplOr, ruleComplAnd - ] - eliminateImplEquiv = somewhere $ useRules - [ ruleDefImpl, ruleDefEquiv - ] - eliminateNots = somewhere $ useRules - [ generalRuleDeMorganAnd, generalRuleDeMorganOr - , ruleDeMorganAnd, ruleDeMorganOr - ] - orToTop = somewhere $ useRules - [ generalRuleAndOverOr, ruleAndOverOr ] - --- A specialized variant of the somewhere traversal combinator. Apply --- the strategy only at (top-level) disjuncts -somewhereOr :: IsStrategy g => g (Context SLogic) -> Strategy (Context SLogic) -somewhereOr s = - let isOr = maybe False (isOperator orOperator) . current - in fix $ \this -> check (Prelude.not . isOr) <*> s - <|> check isOr <*> once this - ---check1, check2 :: (a -> Bool) -> Rule a ---check1 p = minorRule $ makeSimpleRule "check1" $ \a -> if p a then Just a else Nothing ---check2 p = minorRule $ makeSimpleRule "check2" $ \a -> if p a then Just a else Nothing - - ------------------------------------------------------------------------------ --- To DNF, in four steps - -dnfStrategy :: LabeledStrategy (Context SLogic) -dnfStrategy = label "Bring to dnf" - $ label "Eliminate constants" eliminateConstants - <*> label "Eliminate implications/equivalences" eliminateImplEquiv - <*> label "Eliminate nots" eliminateNots - <*> label "Move ors to top" orToTop - where - eliminateConstants = repeat $ topDown $ useRules - [ ruleFalseZeroOr, ruleTrueZeroOr, ruleTrueZeroAnd - , ruleFalseZeroAnd, ruleNotTrue, ruleNotFalse, ruleFalseInEquiv - , ruleTrueInEquiv, ruleFalseInImpl, ruleTrueInImpl - ] - eliminateImplEquiv = repeat $ bottomUp $ useRules - [ ruleDefImpl, ruleDefEquiv - ] - eliminateNots = repeat $ topDown $ - useRules - [ generalRuleDeMorganAnd, generalRuleDeMorganOr ] - |> useRules - [ ruleDeMorganAnd, ruleDeMorganOr - , ruleNotNot - ] - orToTop = repeat $ somewhere $ - liftToContext generalRuleAndOverOr |> - liftToContext ruleAndOverOr - --- local helper function -useRules :: [Rule SLogic] -> Strategy (Context SLogic) +-----------------------------------------------------------------------------+-- Copyright 2010, 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 Domain.Logic.Strategies + ( dnfStrategy, dnfStrategyDWA, somewhereOr+ ) where++import Prelude hiding (repeat)+import Domain.Logic.Rules+import Domain.Logic.GeneralizedRules+import Domain.Logic.Formula+import Common.Context (Context, liftToContext)+import Common.Transformation+import Common.Strategy+import Common.Navigator++-----------------------------------------------------------------------------+-- To DNF, with priorities (the "DWA" approach)++dnfStrategyDWA :: LabeledStrategy (Context SLogic)+dnfStrategyDWA = label "Bring to dnf (DWA)" $ + repeat $ toplevel <|> somewhereOr+ ( label "Simplify" simplify+ |> label "Eliminate implications/equivalences" eliminateImplEquiv+ |> label "Eliminate nots" eliminateNots+ |> label "Move ors to top" orToTop+ )+ where+ toplevel = useRules + [ ruleFalseZeroOr, ruleTrueZeroOr, ruleIdempOr+ , ruleAbsorpOr, ruleComplOr+ ]+ simplify = somewhere $ useRules+ [ ruleFalseZeroOr, ruleTrueZeroOr, ruleTrueZeroAnd+ , ruleFalseZeroAnd, ruleNotTrue, ruleNotFalse+ , ruleNotNot, ruleIdempOr, ruleIdempAnd, ruleAbsorpOr, ruleAbsorpAnd+ , ruleComplOr, ruleComplAnd+ ]+ eliminateImplEquiv = somewhere $ useRules+ [ ruleDefImpl, ruleDefEquiv+ ]+ eliminateNots = somewhere $ useRules+ [ generalRuleDeMorganAnd, generalRuleDeMorganOr+ , ruleDeMorganAnd, ruleDeMorganOr+ ]+ orToTop = somewhere $ useRules + [ generalRuleAndOverOr, ruleAndOverOr ]++-- A specialized variant of the somewhere traversal combinator. Apply +-- the strategy only at (top-level) disjuncts +somewhereOr :: IsStrategy g => g (Context SLogic) -> Strategy (Context SLogic)+somewhereOr s =+ let isOr a = case current a of+ Just (_ :||: _) -> True+ _ -> False+ in fix $ \this -> check (Prelude.not . isOr) <*> s + <|> check isOr <*> once this++--check1, check2 :: (a -> Bool) -> Rule a+--check1 p = minorRule $ makeSimpleRule "check1" $ \a -> if p a then Just a else Nothing+--check2 p = minorRule $ makeSimpleRule "check2" $ \a -> if p a then Just a else Nothing+++-----------------------------------------------------------------------------+-- To DNF, in four steps++dnfStrategy :: LabeledStrategy (Context SLogic)+dnfStrategy = label "Bring to dnf"+ $ label "Eliminate constants" eliminateConstants+ <*> label "Eliminate implications/equivalences" eliminateImplEquiv+ <*> label "Eliminate nots" eliminateNots + <*> label "Move ors to top" orToTop+ where+ eliminateConstants = repeat $ topDown $ useRules+ [ ruleFalseZeroOr, ruleTrueZeroOr, ruleTrueZeroAnd+ , ruleFalseZeroAnd, ruleNotTrue, ruleNotFalse, ruleFalseInEquiv+ , ruleTrueInEquiv, ruleFalseInImpl, ruleTrueInImpl+ ]+ eliminateImplEquiv = repeat $ bottomUp $ useRules+ [ ruleDefImpl, ruleDefEquiv + ] + eliminateNots = repeat $ topDown $ + useRules+ [ generalRuleDeMorganAnd, generalRuleDeMorganOr ]+ |> useRules+ [ ruleDeMorganAnd, ruleDeMorganOr+ , ruleNotNot+ ]+ orToTop = repeat $ somewhere $ + liftToContext generalRuleAndOverOr |> + liftToContext ruleAndOverOr+ +-- local helper function+useRules :: [Rule SLogic] -> Strategy (Context SLogic) useRules = alternatives . map liftToContext
+ src/Domain/Logic/Views.hs view
@@ -0,0 +1,96 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Domain.Logic.Views where++import Common.View+import Domain.Logic.Formula++------------------------------------------------------------+-- Smart constructors++infixr 2 .<->.+infixr 3 .->. +infixr 4 .||. +infixr 5 .&&.++(.<->.) :: Logic a -> Logic a -> Logic a+T .<->. q = q+F .<->. q = nott q+p .<->. T = p+p .<->. F = nott p+p .<->. q = p :<->: q++(.->.) :: Logic a -> Logic a -> Logic a+T .->. q = q+F .->. _ = T+_ .->. T = T+p .->. F = nott p+p .->. q = p :->: q++(.||.) :: Logic a -> Logic a -> Logic a +T .||. _ = T+F .||. q = q+_ .||. T = T+p .||. F = p+p .||. q = p :||: q++(.&&.) :: Logic a -> Logic a -> Logic a+T .&&. q = q+F .&&. _ = F+p .&&. T = p+_ .&&. F = F+p .&&. q = p :&&: q++nott :: Logic a -> Logic a+nott (Not p) = p+nott p = Not p++-------------------------------------------------+-- Views and transformations++simplify :: Logic a -> Logic a+simplify = foldLogic (Var, (.->.), (.<->.), (.&&.), (.||.), nott, T, F)++pushNotWith :: (a -> Logic a) -> Logic a -> Logic a+pushNotWith f = foldLogic (Var, (.->.), (.<->.), (.&&.), (.||.), rec, T, F)+ where+ rec logic = + case logic of+ Not p :<->: q -> p .<->. q+ p :<->: Not q -> p .<->. q+ p :<->: q -> rec p .<->. q+ p :->: q -> p .&&. rec q+ p :||: q -> rec p .&&. rec q+ p :&&: q -> rec p .||. rec q+ Not p -> p+ T -> F+ F -> T+ Var a -> f a++pushNot :: Logic a -> Logic a+pushNot = pushNotWith (nott . Var)+ +orView :: View (Logic a) [a]+orView = newView "logic.orView" (($ []) . f) (foldr ((.||.). Var) F)+ where+ f (p :||: q) = (>>= f p) . f q+ f (Var a) = return . (a:)+ f F = return+ f _ = const Nothing++andView :: View (Logic a) [a]+andView = newView "logic.andView" (($ []) . f) (foldr ((.&&.). Var) T)+ where+ f (p :&&: q) = (>>= f p) . f q+ f (Var a) = return . (a:)+ f T = return+ f _ = const Nothing
src/Domain/Math/Approximation.hs view
@@ -23,8 +23,8 @@ -- Precision of a floating-point number precision :: Int -> Double -> Double-precision n = (/a) . fromIntegral . round . (*a)- where a = 10 Prelude.^ (max 0 n)+precision n = (/a) . fromInteger . round . (*a)+ where a = 10 Prelude.^ max 0 n ------------------------------------------------------------ -- Stop criteria
src/Domain/Math/Clipboard.hs view
@@ -18,12 +18,12 @@ -- generalized interface , addToClipboardG, addListToClipboardG , lookupClipboardG, lookupListClipboardG+ , maybeOnClipboardG ) where import Common.Context import Control.Monad import Common.Rewriting-import Common.Rewriting.Term (Term) import Data.Maybe import Domain.Math.Data.Relation import Domain.Math.Expr@@ -47,7 +47,7 @@ modifyExprVar :: IsTerm a => ExprVar a -> (a -> a) -> ContextMonad () modifyExprVar (ExprVar var) f =- let safe f a = fromMaybe a (f a)+ let safe h a = fromMaybe a (h a) g = fmap (toTerm . f) . fromTerm in modifyVar var (safe g) @@ -104,6 +104,11 @@ m <- readExprVar clipboard expr <- maybeCM (M.lookup (Key s) m) fromExpr expr- ++maybeOnClipboardG :: IsTerm a => String -> ContextMonad (Maybe a)+maybeOnClipboardG s = do + m <- readExprVar clipboard+ return (M.lookup (Key s) m >>= fromExpr)+ lookupListClipboardG :: IsTerm a => [String] -> ContextMonad [a] lookupListClipboardG = mapM lookupClipboardG
src/Domain/Math/Data/Interval.hs view
@@ -30,6 +30,7 @@ , testMe ) where +import Common.TestSuite import Common.Utils (commaList) import Control.Monad import Data.Maybe@@ -69,8 +70,8 @@ fmap f (IS xs) = IS (map (fmap f) xs) showLeft, showRight :: Show a => Endpoint a -> String-showLeft (Excluding a) = "(" ++ show a-showLeft (Including a) = "[" ++ show a+showLeft (Excluding a) = '(' : show a+showLeft (Including a) = '[' : show a showLeft Unbounded = "(-inf" showRight (Excluding a) = show a ++ ")" showRight (Including a) = show a ++ "]"@@ -203,11 +204,11 @@ isInInterval :: Ord a => a -> Interval a -> Bool isInInterval _ Empty = False-isInInterval a (I b c) = f GT b && f LT c+isInInterval a (I x y) = f GT x && f LT y where- f value x = - let g c = (c==EQ && isIncluding x) || c==value - in maybe True (g . compare a) (getPoint x)+ f value b = + let g c = (c==EQ && isIncluding b) || c==value + in maybe True (g . compare a) (getPoint b) --------------------------------------------------------------------- -- Local helper functions@@ -269,9 +270,9 @@ , (1, return Unbounded) ] instance (CoArbitrary a, Ord a) => CoArbitrary (Endpoint a) where- coarbitrary (Excluding a) = variant 0 . coarbitrary a- coarbitrary (Including a) = variant 1 . coarbitrary a- coarbitrary Unbounded = variant 2+ coarbitrary (Excluding a) = variant (0 :: Int) . coarbitrary a+ coarbitrary (Including a) = variant (1 :: Int) . coarbitrary a+ coarbitrary Unbounded = variant (2 :: Int) instance (Arbitrary a, Ord a) => Arbitrary (Interval a) where arbitrary = frequency @@ -279,8 +280,8 @@ , (5, liftM2 makeInterval arbitrary arbitrary) ] instance (CoArbitrary a, Ord a) => CoArbitrary (Interval a) where- coarbitrary Empty = variant 0- coarbitrary (I a b) = variant 1 . coarbitrary a . coarbitrary b+ coarbitrary Empty = variant (0 :: Int)+ coarbitrary (I a b) = variant (1 :: Int) . coarbitrary a . coarbitrary b instance (Arbitrary a, Ord a) => Arbitrary (Intervals a) where arbitrary = do@@ -290,42 +291,42 @@ instance (CoArbitrary a, Ord a) => CoArbitrary (Intervals a) where coarbitrary (IS xs) = coarbitrary xs -testMe :: IO ()-testMe = do- putStrLn "** Intervals"- -- Constructor functions- quickCheck $ op0 empty (const False)- quickCheck $ op0 unbounded (const True)+testMe :: TestSuite+testMe = suite "Intervals" $ do++ suite "Constructor functions" $ do+ addProperty "empty" $ op0 empty (const False)+ addProperty "unbounded" $ op0 unbounded (const True) - quickCheck $ op1 greaterThan (>)- quickCheck $ op1 greaterThanOrEqualTo (>=)- quickCheck $ op1 lessThan (<)- quickCheck $ op1 lessThanOrEqualTo (<=)- quickCheck $ op1 singleton (==)+ addProperty "greater than" $ op1 greaterThan (>)+ addProperty "greater than or equal to" $ op1 greaterThanOrEqualTo (>=)+ addProperty "less than" $ op1 lessThan (<)+ addProperty "less than or equal to" $ op1 lessThanOrEqualTo (<=)+ addProperty "singleton" $ op1 singleton (==) - quickCheck $ op2 open (<) (<)- quickCheck $ op2 closed (<=) (<=)- quickCheck $ op2 leftOpen (<) (<=)- quickCheck $ op2 rightOpen (<=) (<)+ addProperty "open" $ op2 open (<) (<)+ addProperty "closed" $ op2 closed (<=) (<=)+ addProperty "left open" $ op2 leftOpen (<) (<=)+ addProperty "right open" $ op2 rightOpen (<=) (<) - -- From/to lists- quickCheck fromTo1- quickCheck fromTo2+ suite "From/to lists" $ do+ addProperty "" fromTo1+ addProperty "" fromTo2 - -- Combinators- quickCheck defExcept- quickCheck defUnion- quickCheck defIntersect- quickCheck defComplement+ suite "Combinators" $ do+ addProperty "except" defExcept+ addProperty "union" defUnion+ addProperty "intersect" defIntersect+ addProperty "complement" defComplement - -- Combinator properties- quickCheck $ selfInverse complement- quickCheck $ transitive union- quickCheck $ commutative union- quickCheck $ absorption union- quickCheck $ transitive intersect- quickCheck $ commutative intersect- quickCheck $ absorption intersect+ suite "Combinator properties" $ do+ addProperty "inverse complement" $ selfInverse complement+ addProperty "transitive union" $ transitive union+ addProperty "commutative union" $ commutative union+ addProperty "absorption union" $ absorption union+ addProperty "transitive intersect" $ transitive intersect+ addProperty "commutative intersect" $ commutative intersect+ addProperty "absorption intersect" $ absorption intersect fromTo1, fromTo2 :: Intervals Int -> Bool fromTo1 a = fromList (toList a) == a
src/Domain/Math/Data/OrList.hs view
@@ -13,14 +13,14 @@ ( OrList , orList, (\/), true, false , isTrue, isFalse- , disjunctions, normalize, idempotent- , orView+ , disjunctions, normalize, idempotent, fromBool+ , oneDisjunct, orListView ) where import Common.View import Control.Monad-import Common.Traversable-import Common.Rewriting.Term+import Common.Classes+import Common.Rewriting import qualified Domain.Logic.Formula as Logic import Domain.Logic.Formula (Logic((:||:))) import Test.QuickCheck@@ -67,6 +67,15 @@ idempotent T = T idempotent (OrList xs) = OrList (nub xs) +oneDisjunct :: Monad m => (a -> m (OrList a)) -> OrList a -> m (OrList a)+oneDisjunct f xs = + case disjunctions xs of + Just [a] -> f a+ _ -> fail "oneDisjunct"++fromBool :: Bool -> OrList a+fromBool b = if b then true else false+ ------------------------------------------------------------ -- Instances @@ -74,6 +83,8 @@ joinOr :: OrList (OrList a) -> OrList a joinOr = maybe T (foldr (\/) false) . disjunctions +instance Rewrite a => Rewrite (OrList a)+ instance Functor OrList where fmap _ T = T fmap f (OrList xs) = OrList (map f xs)@@ -82,9 +93,6 @@ return = OrList . return m >>= f = joinOr (fmap f m) -instance Once OrList where- onceM = useOnceJoin- instance Switch OrList where switch T = return T switch (OrList xs) = liftM orList (sequence xs)@@ -93,17 +101,9 @@ crush T = [] crush (OrList xs) = xs -instance OnceJoin OrList where- onceJoinM _ T = mzero- onceJoinM f (OrList xs) = rec xs- where- rec [] = mzero- rec (x:xs) = liftM (\/ orList xs) (f x) `mplus`- liftM (return x \/) (rec xs)- instance IsTerm a => IsTerm (OrList a) where- toTerm = toTerm . build orView- fromTerm expr = fromTerm expr >>= matchM orView+ toTerm = toTerm . build orListView+ fromTerm expr = fromTerm expr >>= matchM orListView instance Arbitrary a => Arbitrary (OrList a) where arbitrary = do @@ -111,8 +111,8 @@ xs <- vector n return (OrList xs) instance CoArbitrary a => CoArbitrary (OrList a) where- coarbitrary T = variant 0- coarbitrary (OrList xs) = variant 1 . coarbitrary xs+ coarbitrary T = variant (0 :: Int)+ coarbitrary (OrList xs) = variant (1 :: Int) . coarbitrary xs instance Show a => Show (OrList a) where show T = "true"@@ -123,8 +123,8 @@ ------------------------------------------------------------ -- View to the logic data type -orView :: View (Logic a) (OrList a)-orView = makeView f g +orListView :: View (Logic a) (OrList a)+orListView = makeView f g where f p = case p of Logic.Var a -> return (return a)
src/Domain/Math/Data/Polynomial.hs view
@@ -11,7 +11,7 @@ ----------------------------------------------------------------------------- module Domain.Math.Data.Polynomial ( Polynomial, var, con, raise, power, scale- , degree, coefficient, terms+ , degree, lowestDegree, coefficient, terms , isMonic, toMonic, isRoot, positiveRoots, negativeRoots , derivative, eval, division, longDivision, polynomialGCD , factorize@@ -21,7 +21,7 @@ import qualified Data.IntSet as IS import Data.Char import Control.Monad-import Common.Traversable+import Common.Classes import Data.List (nub) import Data.Ratio (approxRational) import Domain.Math.Approximation (newton, within)@@ -50,9 +50,6 @@ instance Functor Polynomial where fmap f (P m) = P (IM.map f m) -instance Once Polynomial where- onceM f (P m) = liftM P (onceM f m)- instance Switch Polynomial where switch (P m) = liftM P (switch m) @@ -97,6 +94,13 @@ | otherwise = IS.findMax is where is = IM.keysSet m +lowestDegree :: Polynomial a -> Int+lowestDegree (P m)+ | IS.null is = 0+ | otherwise = IS.findMin is+ where is = IM.keysSet m++ coefficient :: Num a => Int -> Polynomial a -> a coefficient n (P m) = IM.findWithDefault 0 n m @@ -158,7 +162,7 @@ -- polynomial long division, where p2 is monic monicLongDivision :: Num a => Polynomial a -> Polynomial a -> (Polynomial a, Polynomial a) monicLongDivision p1 p2- | d1 >= d2 && isMonic p2 = (toP quot, toP rem)+ | d1 >= d2 && isMonic p2 = (toP quotient, toP remainder) | otherwise = error $ "invalid monic division" ++ show (p1, p2) where d1 = degree p1@@ -166,7 +170,7 @@ xs = map (`coefficient` p1) [d1, d1-1 .. 0] ys = drop 1 $ map (negate . (`coefficient` p2)) [d2, d2-1 .. 0] - (quot, rem) = rec [] xs+ (quotient, remainder) = rec [] xs toP = P . IM.filter (/= 0) . IM.fromAscList . zip [0..] rec acc (a:as) | length as >= length ys = @@ -203,10 +207,10 @@ , Just p2 <- [division p p1] ] - candidateRoots :: Polynomial Rational -> [Rational]- candidateRoots p = nub (map (`approxRational` 0.0001) xs)- where- f = eval (fmap fromRational p)- df = eval (fmap fromRational (derivative p))- xs = nub (map (within 0.0001 . take 10 . newton f df) startList)- startList = [0, 3, -3, 10, -10, 100, -100]+candidateRoots :: Polynomial Rational -> [Rational]+candidateRoots p = nub (map (`approxRational` 0.0001) xs)+ where+ f = eval (fmap fromRational p)+ df = eval (fmap fromRational (derivative p))+ xs = nub (map (within 0.0001 . take 10 . newton f df) startList)+ startList = [0, 3, -3, 10, -10, 100, -100]
src/Domain/Math/Data/PrimeFactors.hs view
@@ -13,10 +13,13 @@ ( PrimeFactors , factors, multiplicity, coprime , square, power, splitPower- , primes+ , primes, greatestPower, allPowers ) where import qualified Data.IntMap as IM+import Common.Utils+import Control.Monad+import Data.Maybe ------------------------------------------------------------- -- Representation@@ -35,9 +38,9 @@ -- Conversion to and from factors toFactors :: Integer -> Factors-toFactors n- | n > 0 = rec primes n- | n < 0 = rec primes (-n)+toFactors a+ | a > 0 = rec primes a+ | a < 0 = rec primes (-a) | otherwise = IM.singleton 0 1 where rec [] n = IM.singleton (fromIntegral n) 1@@ -56,7 +59,7 @@ fromFactors :: Factors -> Integer fromFactors = product . map f . IM.toList- where f (a, i) = fromIntegral a ^ fromIntegral i+ where f (a, i) = toInteger a ^ toInteger i -- For practical reasons, the list of prime numbers is cut-off after -- 1000 elements (last primes gives 7919).@@ -121,6 +124,28 @@ power :: PrimeFactors -> Int -> PrimeFactors power (PF a m) i = PF (a^i) (IM.map (*i) m)++greatestPower :: Integer -> Maybe (Integer, Integer)+greatestPower n = do+ guard $ n > 1+ let (as, xs) = unzip $ factors $ fromInteger n+ x <- safeHead xs+ guard $ allsame xs && x > 1+ return (fromIntegral (product as), fromIntegral x)++-- n == a^x with (a,x) == greatestPower n+-- prop_greatestPower n = traceShow n $ +-- maybe True (\(a,x) -> fromIntegral a ^ fromIntegral x == n) $ greatestPower n ++allPowers :: Integer -> [(Integer, Integer)]+allPowers n = do+ (b, e) <- maybeToList $ greatestPower n + let f i = let (a, r) = e `divMod` i+ in if a > 1 && r == 0 then Just (b^i, a) else Nothing+ mapMaybe f [1..e]++-- prop_allPowers n = traceShow n $ +-- and (map (\(a,x) -> fromIntegral a ^ fromIntegral x == n) (allPowers n)) -- splitPower i a = (b,c) -- => b^i * c = a
src/Domain/Math/Data/Relation.hs view
@@ -16,6 +16,7 @@ Relational(..), mapLeft, mapRight, updateLeft, updateRight -- * Relation data type , Relation, relationType, RelationType(..), relationSymbols + , notRelation, eval -- * Constructor functions , makeType, (.==.), (./=.), (.<.), (.>.), (.<=.), (.>=.), (.~=.) -- * Equation (or equality) @@ -25,10 +26,10 @@ ) where import Common.View -import Common.Rewriting (IsTerm(..), Rewrite) -import Common.Traversable -import Domain.Math.Expr.Symbolic -import qualified Text.OpenMath.Dictionary.Relation1 as Relation1 +import Common.Rewriting +import Common.Classes +import Domain.Math.Expr.Symbols (openMathSymbol) +import Text.OpenMath.Dictionary.Relation1 import Data.Maybe import Test.QuickCheck import Control.Monad @@ -40,7 +41,7 @@ leftHandSide :: f a -> a rightHandSide :: f a -> a flipSides :: f a -> f a -- possibly also flips operator - constructor :: f a -> (b -> b -> f b) + constructor :: f a -> b -> b -> f b isSymmetric :: f a -> Bool -- default definitions isSymmetric _ = False @@ -74,31 +75,56 @@ leftHandSide = lhs rightHandSide = rhs flipSides (R x rt y) = R y (flipRelType rt) x - constructor (R _ rt _) x y = R x rt y + constructor (R _ rt _) = flip R rt isSymmetric = (`elem` [EqualTo, NotEqualTo, Approximately]) . relationType instance IsTerm a => IsTerm (Relation a) where toTerm p = let op = relationType p - sym = maybe (toSymbol (show op)) snd (lookup op relationSymbols) + sym = maybe (newSymbol (show op)) snd (lookup op relationSymbols) in binary sym (toTerm (leftHandSide p)) (toTerm (rightHandSide p)) - fromTerm a = - let f (relType, (_, s)) = do - (e1, e2) <- isBinary s a - liftM2 (makeType relType) (fromTerm e1) (fromTerm e2) - in msum (map f relationSymbols) + fromTerm term = + case getFunction term of + Just (s, [a, b]) -> + case [ rt | (rt, (_, t)) <- relationSymbols, s==t ] of + [rt] -> liftM2 (makeType rt) (fromTerm a) (fromTerm b) + _ -> fail "fromTerm: relation" + _ -> fail "fromTerm: relation" instance Rewrite a => Rewrite (Relation a) relationSymbols :: [(RelationType, (String, Symbol))] relationSymbols = - [ (EqualTo, ("==", eqSymbol)), (NotEqualTo, ("/=", neqSymbol)) - , (LessThan, ("<", ltSymbol)), (GreaterThan, (">", gtSymbol)) - , (LessThanOrEqualTo, ("<=", leqSymbol)) - , (GreaterThanOrEqualTo, (">=", geqSymbol)) - , (Approximately, ("~=", approxSymbol)) + [ (EqualTo, ("==", openMathSymbol eqSymbol)) + , (NotEqualTo, ("/=", openMathSymbol neqSymbol)) + , (LessThan, ("<", openMathSymbol ltSymbol)) + , (GreaterThan, (">", openMathSymbol gtSymbol)) + , (LessThanOrEqualTo, ("<=", openMathSymbol leqSymbol)) + , (GreaterThanOrEqualTo, (">=", openMathSymbol geqSymbol)) + , (Approximately, ("~=", openMathSymbol approxSymbol)) ] +notRelation :: Relation a -> Relation a +notRelation r = r { relationType = relationType r ? table } + where + table = xs ++ map swap xs ++ [(Approximately, Approximately)] + swap (x, y) = (y, x) + xs = [ (EqualTo, NotEqualTo) + , (LessThan, GreaterThanOrEqualTo) + , (LessThanOrEqualTo, GreaterThan) + ] + +eval :: Ord a => RelationType -> a -> a -> Bool +eval relType = + case relType of + EqualTo -> (==) + NotEqualTo -> (/=) + LessThan -> (<) + GreaterThan -> (>) + LessThanOrEqualTo -> (<=) + GreaterThanOrEqualTo -> (>=) + Approximately -> (==) + -- helpers showRelType :: RelationType -> String showRelType = fst . (? relationSymbols) @@ -115,18 +141,12 @@ ----------------------------------------------------------------------------- -- Traversable instance declarations -instance Once Relation where onceM = onceMRelation instance Switch Relation where switch = switchRelation instance Crush Relation where crush = crushRelation switchRelation :: (Relational f, Monad m) => f (m a) -> m (f a) switchRelation p = liftM2 (constructor p) (leftHandSide p) (rightHandSide p) - -onceMRelation :: (Relational f, MonadPlus m) => (a -> m a) -> f a -> m (f a) -onceMRelation f p = - liftM (`updateLeft` p) (f (leftHandSide p)) `mplus` - liftM (`updateRight` p) (f (rightHandSide p)) crushRelation :: Relational f => f a -> [a] crushRelation p = [leftHandSide p, rightHandSide p] @@ -176,7 +196,10 @@ instance Functor Equation where fmap f (x :==: y) = f x :==: f y - + +instance Zip Equation where + fzipWith f (a :==: b) (c :==: d) = f a c :==: f b d + instance Relational Equation where leftHandSide = leftHandSide . build equationView rightHandSide = rightHandSide . build equationView @@ -184,7 +207,6 @@ constructor = const (:==:) isSymmetric = const True -instance Once Equation where onceM = onceMRelation instance Switch Equation where switch = switchRelation instance Crush Equation where crush = crushRelation @@ -232,7 +254,6 @@ let relType = relationType (build inequalityView ineq) in fst (relType ? inequalityTable) -instance Once Inequality where onceM = onceMRelation instance Switch Inequality where switch = switchRelation instance Crush Inequality where crush = crushRelation @@ -264,17 +285,4 @@ inequalityTable = [ (LessThan, ((:<:), (.<.))), (LessThanOrEqualTo, ((:<=:), (.<=.))) , (GreaterThan, ((:>:), (.>.))), (GreaterThanOrEqualTo, ((:>=:), (.>=.))) - ] - ------------------------------------------------------------------------------ --- OpenMath symbols - -eqSymbol, ltSymbol, gtSymbol, neqSymbol, leqSymbol, - geqSymbol, approxSymbol :: Symbol -eqSymbol = toSymbol Relation1.eqSymbol -ltSymbol = toSymbol Relation1.ltSymbol -gtSymbol = toSymbol Relation1.gtSymbol -neqSymbol = toSymbol Relation1.neqSymbol -leqSymbol = toSymbol Relation1.leqSymbol -geqSymbol = toSymbol Relation1.geqSymbol -approxSymbol = toSymbol Relation1.approxSymbol+ ]
src/Domain/Math/Data/SquareRoot.hs view
@@ -80,14 +80,14 @@ recipSqMap m = case M.toList m of [] -> error "division by zero"- [(n, x)] -> M.singleton n (recip (x Prelude.* fromIntegral n))- _ -> (a-b) * recipSqMap (makeMap ((a*a) - (b*b)))+ [(n, x)] -> M.singleton n (recip (x * fromIntegral n))+ _ -> (a .-. b) .*. recipSqMap (makeMap ((a .*. a) .-. (b .*. b))) where (ys, zs) = splitAt (length xs `div` 2) xs (a, b) = (M.fromList ys, M.fromList zs) xs = M.toList m- (*) = timesSqMap- (-) = minusSqMap+ (.*.) = timesSqMap+ (.-.) = minusSqMap sqrtPF :: Num a => P.PrimeFactors -> SqMap a sqrtPF n@@ -100,13 +100,13 @@ -- Type class instances instance Num a => Show (SquareRoot a) where- show (S b m) = g (map f (M.toList m)) ++ imPart+ show (S isNeg m) = g (map f (M.toList m)) ++ imPart where f (n, a) = ( signum a == -1 , times (guard (abs a /= 1) >> Just (show (abs a))) (guard (n /= 1) >> Just ("sqrt(" ++ show (toInteger n) ++ ")")) )- imPart = if b then " (imaginary number)" else "" + imPart = if isNeg then " (imaginary number)" else "" g [] = "0" g ((b,x):xs) = (if b then "-" else "") ++ x ++ concatMap h xs h (b, x) = (if b then " - " else " + ") ++ x@@ -147,7 +147,7 @@ fromSquareRoot :: Num a => SquareRoot a -> Maybe a fromSquareRoot a = case toList a of- [(a, n)] | n==1 -> Just a + [(b, n)] | n==1 -> Just b [] -> Just 0 _ -> Nothing @@ -165,7 +165,7 @@ scale a sr = if a==0 then 0 else fmap (*a) sr isqrt :: Integer -> Integer-isqrt = floor . Prelude.sqrt . fromInteger+isqrt = (floor :: Double -> Integer) . Prelude.sqrt . fromInteger sqrtRational :: Fractional a => Rational -> SquareRoot a sqrtRational r = scale (1/fromIntegral b) (sqrt (a*b))
+ src/Domain/Math/Derivative/Exercises.hs view
@@ -0,0 +1,219 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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 Domain.Math.Derivative.Exercises + ( derivativeExercise, derivativePolyExercise + , derivativeProductExercise, derivativeQuotientExercise + , derivativePowerExercise + ) where + +import Common.Library +import Common.Uniplate +import Control.Monad +import Data.List +import Data.Maybe +import Data.Ord +import Domain.Math.Derivative.Rules +import Domain.Math.Derivative.Strategies +import Domain.Math.Examples.DWO5 +import Domain.Math.Expr +import Domain.Math.Numeric.Views +import Domain.Math.Polynomial.CleanUp +import Domain.Math.Polynomial.Generators +import Domain.Math.Polynomial.RationalExercises +import Domain.Math.Polynomial.Views +import Prelude hiding (repeat, (^)) +import Test.QuickCheck + +derivativePolyExercise :: Exercise Expr +derivativePolyExercise = describe + "Find the derivative of a polynomial. First normalize the polynomial \ + \(e.g., with distribution). Don't make use of the product-rule, or \ + \other chain rules." $ makeExercise + { exerciseId = diffId # "polynomial" + , status = Provisional + , parser = parseExpr + , isReady = (`belongsTo` polyNormalForm rationalView) + , isSuitable = isPolyDiff + , equivalence = eqPolyDiff + , similarity = simPolyDiff + , strategy = derivativePolyStrategy + , navigation = navigator + , examples = concat (diffSet1 ++ diffSet2 ++ diffSet3) + , testGenerator = Just $ liftM (diff . lambda (Var "x")) $ + sized quadraticGen + } + +derivativeProductExercise :: Exercise Expr +derivativeProductExercise = describe + "Use the product-rule to find the derivative of a polynomial. Keep \ + \the parentheses in your answer." $ + derivativePolyExercise + { exerciseId = diffId # "product" + , isReady = noDiff + , strategy = derivativeProductStrategy + , examples = concat diffSet3 + } + +derivativeQuotientExercise :: Exercise Expr +derivativeQuotientExercise = describe + "Use the quotient-rule to find the derivative of a polynomial. Only \ + \remove parentheses in the numerator." $ + derivativePolyExercise + { exerciseId = diffId # "quotient" + , isReady = readyQuotientDiff + , isSuitable = isQuotientDiff + , equivalence = eqQuotientDiff + , strategy = derivativeQuotientStrategy + , ruleOrdering = ruleOrderingWithId [ruleDerivQuotient] + , examples = concat diffSet4 + , testGenerator = Nothing + } + +derivativePowerExercise :: Exercise Expr +derivativePowerExercise = describe + "First write as a power, then find the derivative. Rewrite negative or \ + \rational exponents." $ + derivativePolyExercise + { exerciseId = diffId # "power" + , status = Experimental + , isReady = \a -> noDiff a && onlyNatPower a + , isSuitable = const True + , equivalence = \_ _ -> True -- \x y -> eqApprox (evalDiff x) (evalDiff y) + , strategy = derivativePowerStrategy + , examples = concat (diffSet5 ++ diffSet6) + , testGenerator = Nothing + } + +derivativeExercise :: Exercise Expr +derivativeExercise = makeExercise + { exerciseId = describe "Derivative" diffId + , status = Experimental + , parser = parseExpr + , isReady = noDiff + , strategy = derivativeStrategy + , ruleOrdering = derivativeOrdering + , navigation = navigator + , examples = concat (diffSet3++diffSet4++ + diffSet5++diffSet6++diffSet7++diffSet8) + } + +derivativeOrdering :: Rule a -> Rule a -> Ordering +derivativeOrdering = comparing f + where + f a = (getId a /= j, getId a == i, showId a) + i = getId ruleDefRoot + j = getId ruleDerivPolynomial + +isPolyDiff :: Expr -> Bool +isPolyDiff = maybe False (`belongsTo` polyViewWith rationalView) . getDiffExpr + +isQuotientDiff :: Expr -> Bool +isQuotientDiff de = fromMaybe False $ do + expr <- getDiffExpr de + xs <- match sumView expr + let f a = maybe [a] (\(x, y) -> [x, y]) (match divView a) + ys = concatMap f xs + isp = (`belongsTo` polyViewWith rationalView) + return (all isp ys) + +eqPolyDiff :: Expr -> Expr -> Bool +eqPolyDiff x y = + let f a = fromMaybe (descend f a) (apply ruleDerivPolynomial a) + in viewEquivalent (polyViewWith rationalView) (f x) (f y) + +eqQuotientDiff :: Expr -> Expr -> Bool +eqQuotientDiff a b = eqSimplifyRational (make a) (make b) + where + make = inContext derivativeQuotientExercise . f + rs = [ ruleDerivPolynomial, ruleDerivQuotient, ruleDerivProduct + , ruleDerivNegate, ruleDerivPlus, ruleDerivMin + ] + f x = case mapMaybe (`apply` x) rs of + hd:_ -> f hd + [] -> descend f x + +readyQuotientDiff :: Expr -> Bool +readyQuotientDiff expr = fromMaybe False $ do + xs <- match sumView expr + let f a = fromMaybe (a, 1) (match divView a) + (ys, zs) = unzip (map f xs) + isp = (`belongsTo` polyViewWith rationalView) + nfp = (`belongsTo` polyNormalForm rationalView) + return (all nfp ys && all isp zs) + +simPolyDiff :: Expr -> Expr -> Bool +simPolyDiff x y = + let f = acExpr . cleanUpExpr + in f x == f y + +noDiff :: Expr -> Bool +noDiff e = null [ () | Sym s _ <- universe e, isDiffSymbol s ] + +onlyNatPower :: Expr -> Bool +onlyNatPower e = and [ isNat a | Sym s [_, a] <- universe e, isPowerSymbol s ] + where + isNat (Nat _) = True + isNat _ = False + +{- +evalDiff :: Expr -> Expr +evalDiff expr + | isDiff expr = + case concatMap (`applyAll` expr) list of + hd:_ -> evalDiff hd + _ -> expr + | otherwise = descend evalDiff expr + where + list = [ ruleDerivPolynomial, ruleDerivPowerFactor + , ruleDerivPlus, ruleDerivMin, ruleDerivNegate + , ruleDerivProduct, ruleDerivQuotient + , ruleDerivPowerChain, ruleDerivSqrtChain, ruleDerivRoot + ] + +go = checkExercise derivativePowerExercise + +raar i = printDerivation derivativePowerExercise expr + where + expr = examples derivativePowerExercise !! i + +eqApprox :: Expr -> Expr -> Bool +eqApprox a b = rec 5 doubleList + where + vs = nub (collectVars a ++ collectVars b) + + rec 0 = const True + rec n = rec2 n 10 + + rec2 _ 0 ds = undefined -- a==b + rec2 n m ds = case eqApproxWith f a b of + Just b -> b && rec (n-1) ys + Nothing -> rec2 n (m-1) ys + where + (xs, ys) = splitAt (length vs) ds + f = (xs !!) . fromMaybe 0 . (`elemIndex` vs) + +eqApproxWith :: (String -> Double) -> Expr -> Expr -> Maybe Bool +eqApproxWith f a b = do + d1 <- match doubleView (subst a) + d2 <- match doubleView (subst b) + return $ abs (d1 - d2) < 1e-9 -- 11 is still ok for example set + where + subst (Var s) = Number (f s) + subst expr = descend subst expr + +doubleList :: [Double] -- between -20 and 20 +doubleList = iterate next (pi*exp 1) + where + next :: Double -> Double + next a = if b > 20 then b-20 else b + where + b = a + exp 3 * log 2 -}
+ src/Domain/Math/Derivative/Rules.hs view
@@ -0,0 +1,210 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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 Domain.Math.Derivative.Rules where + +import Prelude hiding ((^)) +import Common.Transformation +import Common.View +import Control.Monad +import Domain.Math.Expr +import Common.Id +import Common.Rewriting +import Data.Maybe +import Domain.Math.Polynomial.Views +import Domain.Math.Numeric.Views +import Domain.Math.Data.Polynomial +import Domain.Math.Power.Views +import Domain.Math.Power.Utils ( (<&>) ) + + +derivativeRules :: [Rule Expr] +derivativeRules = + [ ruleDerivCon, ruleDerivPlus, ruleDerivMin, ruleDerivNegate + , ruleDerivMultiple, ruleDerivPower, ruleDerivVar + , ruleDerivProduct, ruleDerivQuotient, ruleDerivPowerChain + , ruleSine, ruleLog, ruleDerivSqrt, ruleDerivSqrtChain + ] + +diff :: Expr -> Expr +diff = unary diffSymbol + +ln :: Expr -> Expr +ln = unary lnSymbol + +lambda :: Expr -> Expr -> Expr +lambda = binary lambdaSymbol + +diffId :: Id +diffId = newId "calculus.differentiation" + +isDiffSymbol, isLambdaSymbol :: Symbol -> Bool +isDiffSymbol = (== diffSymbol) +isLambdaSymbol = (== lambdaSymbol) + +----------------------------------------------------------------- +-- Rules for Diffs + +ruleSine :: Rule Expr +ruleSine = rule (diffId, "sine") $ + \x -> diff (lambda x (sin x)) :~> cos x + +ruleLog :: Rule Expr +ruleLog = rule (diffId, "logarithmic") $ + \x -> diff (lambda x (ln x)) :~> 1/x + +ruleDerivPlus :: Rule Expr +ruleDerivPlus = rule (diffId, "plus") $ + \x f g -> diff (lambda x (f + g)) :~> diff (lambda x f) + diff (lambda x g) + +ruleDerivMin :: Rule Expr +ruleDerivMin = rule (diffId, "min") $ + \x f g -> diff (lambda x (f - g)) :~> diff (lambda x f) - diff (lambda x g) + +ruleDerivNegate :: Rule Expr +ruleDerivNegate = rule (diffId, "negate") $ + \x f -> diff (lambda x (-f)) :~> -diff (lambda x f) + +ruleDerivVar :: Rule Expr +ruleDerivVar = rule (diffId, "var") $ + \x -> diff (lambda x x) :~> 1 + +ruleDerivProduct :: Rule Expr +ruleDerivProduct = rule (diffId, "product") $ + \x f g -> diff (lambda x (f * g)) :~> diff (lambda x f)*g + f*diff (lambda x g) + +-- The second rewrite rule should not have been necessary, except that cleaning +-- up an expression will typically put the negate in front of the division: this +-- makes sure the rule is triggered anyway. +ruleDerivQuotient :: Rule Expr +ruleDerivQuotient = ruleList (diffId, "quotient") + [ \x f g -> diff (lambda x (f/g)) :~> (g*diff (lambda x f) - f*diff (lambda x g)) / (g^2) + , \x f g -> diff (lambda x (-f/g)) :~> (g*diff (lambda x (-f)) - (-f)*diff (lambda x g)) / (g^2) + ] + +ruleDerivPolynomial :: Rule Expr +ruleDerivPolynomial = describe "This rule returns the derivative for all \ + \expressions that can be turned into a polynomial (of rational numbers). \ + \The polynomial does not have to be in standard form." $ + makeSimpleRule (diffId, "deriv-of-poly") f + where + f (Sym d [Sym l [Var v, expr]]) | isDiffSymbol d && isLambdaSymbol l = do + let myView = polyViewWith rationalView + (s, p) <- match myView expr + guard (s==v) + return (build myView (s, derivative p)) + f _ = Nothing + +----------------------------------- +-- Special rules (not defined with unification) + +ruleDerivCon :: Rule Expr +ruleDerivCon = makeSimpleRule (diffId, "constant") f + where + f (Sym d [Sym l [Var v, e]]) + | isDiffSymbol d && isLambdaSymbol l && withoutVar v e = return 0 + f _ = Nothing + +ruleDerivMultiple :: Rule Expr +ruleDerivMultiple = makeSimpleRule (diffId, "constant-multiple") f + where + f (Sym d [Sym l [x@(Var v), n :*: e]]) + | isDiffSymbol d && isLambdaSymbol l && withoutVar v n = + return $ n * diff (lambda x e) + f (Sym d [Sym l [x@(Var v), e :*: n]]) + | isDiffSymbol d && isLambdaSymbol l && withoutVar v n = + return $ n * diff (lambda x e) + f _ = Nothing + +ruleDerivPower :: Rule Expr +ruleDerivPower = makeSimpleRule (diffId, "power") f + where + f (Sym d [Sym l [x@(Var v), Sym p [x1, n]]]) + | isDiffSymbol d && isLambdaSymbol l && isPowerSymbol p && x==x1 && withoutVar v n = + return $ n * (x ^ (n-1)) + f _ = Nothing + +ruleDerivPowerChain :: Rule Expr +ruleDerivPowerChain = makeSimpleRule (diffId, "chain-power") f + where + f (Sym d [Sym l [x@(Var v), Sym p [a, n]]]) + | isDiffSymbol d && isLambdaSymbol l && isPowerSymbol p && withoutVar v n = + return $ n * (a ^ (n-1)) * diff (lambda x a) + f _ = Nothing + +ruleDerivSqrt :: Rule Expr +ruleDerivSqrt = makeSimpleRule (diffId, "sqrt") f + where + f (Sym d [Sym l [x@(Var _), Sqrt x1]]) + | isDiffSymbol d && isLambdaSymbol l && x==x1 = + return $ 1 / (2 * sqrt x) + f _ = Nothing + +ruleDerivSqrtChain :: Rule Expr +ruleDerivSqrtChain = makeSimpleRule (diffId, "chain-sqrt") f + where + f (Sym d [Sym l [x@(Var _), Sqrt a]]) + | isDiffSymbol d && isLambdaSymbol l = + return $ (1 / (2 * sqrt a)) * diff (lambda x a) + f _ = Nothing + +ruleDefRoot :: Rule Expr +ruleDefRoot = rule (diffId, "def-root") $ + \a b -> root a b :~> a ^ (1/b) + +ruleDerivRoot :: Rule Expr +ruleDerivRoot = rule (diffId, "def-root") $ + \a b x -> diff (lambda x (root a b)) :~> diff (lambda x (a ^ (1/b))) + +ruleDerivPowerFactor :: Rule Expr +ruleDerivPowerFactor = makeSimpleRule (diffId, "power-factor") $ \de -> do + expr <- getDiffExpr de + (a, x, r) <- match myPowerView expr + return $ build myPowerView (a*fromRational r, x, r-1) + +-- (a+b)/c ~> a/c + b/c +ruleSplitRational :: Rule Expr +ruleSplitRational = makeSimpleRule (diffId, "split-rational") $ \expr -> do + (up, c) <- match divView expr + (a, b) <- match plusView up + return (a/c + b/c) + +myPowerView :: View Expr (Expr, String, Rational) +myPowerView = makeView f g + where + f expr = case match timesView expr of + Just (a, b) -> do + guard (hasNoVar a) + (x, r) <- match powView b + return (a, x, r) + `mplus` do + guard (hasNoVar b) + (x, r) <- match powView a + return (b, x, r) + Nothing -> do + (x, r) <- match powView expr + return (1, x, r) + g (a, x, r) = a .*. (Var x .^. fromRational r) + + powView = (powerView <&> noPowerView) >>> myVarView *** rationalView + myVarView = makeView isVar Var + noPowerView = makeView (\expr -> Just (expr, 1)) (build powerView) + + isVar (Var x) = Just x + isVar _ = Nothing + +isDiff :: Expr -> Bool +isDiff = isJust . getDiffExpr + +getDiffExpr :: Expr -> Maybe Expr +getDiffExpr (Sym d [Sym l [Var _, expr]]) | + isDiffSymbol d && isLambdaSymbol l = Just expr +getDiffExpr _ = Nothing
+ src/Domain/Math/Derivative/Strategies.hs view
@@ -0,0 +1,102 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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 Domain.Math.Derivative.Strategies + ( derivativeStrategy, derivativePolyStrategy + , derivativeProductStrategy, derivativeQuotientStrategy + , derivativePowerStrategy, getDiffExpr + ) where + +import Common.Library +import Data.Maybe +import Domain.Math.Derivative.Rules +import Domain.Math.Expr +import Domain.Math.Polynomial.CleanUp +import Domain.Math.Polynomial.Views +import Domain.Math.Polynomial.Rules +import Domain.Math.Numeric.Views +import Domain.Math.Power.Strategies +import Domain.Math.Power.Rules + +import Prelude hiding ((^)) + +derivativeStrategy :: LabeledStrategy (Context Expr) +derivativeStrategy = cleanUpStrategy (applyTop cleanUpExpr) $ + label "Derivative" $ repeatS $ somewhere $ + alternatives (map liftToContext derivativeRules) + <|> derivativePolyStepStrategy + <|> check isDiffC <*> once (once (liftToContext ruleDefRoot)) + where + isDiffC = maybe False isDiff . current + +derivativePolyStrategy :: LabeledStrategy (Context Expr) +derivativePolyStrategy = cleanUpStrategy (applyTop cleanUpExpr) $ + label "derivative-polynomial" $ + repeatS (somewhere (alternatives (map liftToContext rulesPolyNF))) + <*> derivativePolyStepStrategy + +rulesPolyNF :: [Rule Expr] +rulesPolyNF = + [ distributionSquare, distributeTimes, merge + , distributeDivision, noDivisionConstant + ] + +derivativeProductStrategy :: LabeledStrategy (Context Expr) +derivativeProductStrategy = cleanUpStrategy (applyTop cleanUpExpr) $ + label "derivative-product" $ + repeatS (somewhere (derivativePolyStepStrategy |> alternatives list)) + where + list = map liftToContext + [ distributeDivision, noDivisionConstant + , ruleDerivProduct, defPowerNat + , ruleDerivNegate, ruleDerivPlus, ruleDerivMin + ] + +derivativeQuotientStrategy :: LabeledStrategy (Context Expr) +derivativeQuotientStrategy = cleanUpStrategy (applyTop cleanUpExpr) $ + label "derivative-quotient" $ + repeatS (somewhere (derivativePolyStepStrategy |> alternatives list)) + <*> repeatS (exceptLowerDiv (alternatives (map liftToContext rulesPolyNF))) + where + list = map liftToContext + [ ruleDerivQuotient, ruleDerivPlus, ruleDerivMin, ruleDerivNegate ] + +derivativePowerStrategy :: LabeledStrategy (Context Expr) +derivativePowerStrategy = label "derivative-power" $ + cleanUpStrategy (applyTop cleanUpExpr) (label "split-rational" + (repeatS (somewhere (liftToContext ruleSplitRational)))) <*> + configure mycfg powerOfStrategy <*> + repeatS (distr <*> configure mycfg powerOfStrategy) <*> + cleanUpStrategy (applyTop cleanUpExpr) (label "use-derivative-rules" + (repeatS (somewhere (alternatives list)))) <*> + configure mycfg nonNegBrokenExpStrategy + where + list = map liftToContext + [ ruleDerivPlus, ruleDerivMin, ruleDerivNegate, ruleDerivPowerFactor + , ruleDerivCon ] + mycfg = [(byName myFractionTimes, Remove)] + distr = cleanUpStrategy (applyTop cleanUpExpr) $ + label "distr" (somewhere (alternatives (map liftToContext rulesPolyNF))) + +derivativePolyStepStrategy :: LabeledStrategy (Context Expr) +derivativePolyStepStrategy = label "derivative-poly-step" $ + check polyDiff <*> liftToContext ruleDerivPolynomial + where + polyDiff = maybe False nfPoly . (>>= getDiffExpr) . current + nfPoly = (`belongsTo` polyNormalForm rationalView) + +exceptLowerDiv :: IsStrategy f => f (Context Expr) -> Strategy (Context Expr) +exceptLowerDiv = somewhereWith "except-lower-div" $ \a -> + if isDivC a then [0] else [0 .. arity a-1] + where + isDivC = maybe False isDiv . current + isDiv (_ :/: _) = True + isDiv _ = False
− src/Domain/Math/DerivativeExercise.hs
@@ -1,66 +0,0 @@-{-# OPTIONS -fno-case-merge #-} ------------------------------------------------------------------------------ --- Copyright 2010, 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 Domain.Math.DerivativeExercise where - -import Common.Uniplate (universe) -import Prelude hiding (repeat, (^)) -import Domain.Math.DerivativeRules -import Common.Strategy (Strategy, somewhere, (<*>), alternatives, label, LabeledStrategy, try) -import qualified Common.Strategy -import Common.Navigator -import Common.Context (Context, liftToContext) -import Common.Exercise -import Common.Transformation -import Control.Monad -import Domain.Math.Simplification -import Domain.Math.Expr - -derivativeExercise :: Exercise Expr -derivativeExercise = makeExercise - { description = "Derivative" - , exerciseCode = makeCode "math" "derivative" - , status = Experimental - , parser = parseExpr - , isReady = noDiff - , extraRules = map liftToContext derivativeRules ++ [tidyup] - , strategy = derivativeStrategy - , navigation = navigator - , examples = [ex1, ex2, ex3, ex4] - } - -noDiff :: Expr -> Bool -noDiff e = null [ () | Sym s _ <- universe e, s == diffSymbol ] - -derivativeStrategy :: LabeledStrategy (Context Expr) -derivativeStrategy = - label "Derivative" $ - try tidyup <*> Common.Strategy.repeat (derivative <*> try tidyup) - -tidyup :: Rule (Context Expr) -tidyup = liftToContext $ makeSimpleRule "Tidy-up rule" $ \old -> - let new = simplify old - in if old==new then Nothing else Just new - -derivative :: Strategy (Context Expr) -derivative = somewhere $ alternatives (map liftToContext derivativeRules) - -ex1, ex2, ex3 :: Expr -ex1 = diff $ lambda (Var "x") $ Var "x" ^ 2 -ex2 = diff $ lambda (Var "x") $ ((1/3) :*: (x ^ fromInteger 3)) :+: (fromInteger (-3) :*: (x ^ fromInteger 2)) :+: x :+: fromInteger (-5) - where x = Var "x" -ex3 = diff $ lambda (Var "x") (2 * Var "x") -ex4 = diff $ lambda (Var "x") (ln (Var "x")) - -main :: IO () -main = forM_ [ex1, ex2, ex3, ex4] $ - printDerivation derivativeExercise
− src/Domain/Math/DerivativeRules.hs
@@ -1,110 +0,0 @@------------------------------------------------------------------------------ --- Copyright 2010, 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 Domain.Math.DerivativeRules where - -import Prelude hiding ((^)) -import Common.Transformation -import Domain.Math.Expr -import Common.Rewriting - -derivativeRules :: [Rule Expr] -derivativeRules = - [ ruleDerivCon, ruleDerivPlus, ruleDerivMin - , ruleDerivMultiple, ruleDerivPower, ruleDerivVar - , ruleDerivProduct, ruleDerivQuotient {-, ruleDerivChain-}, ruleDerivChainPowerExprs - , ruleSine, ruleLog - ] - -diff :: Expr -> Expr -diff = unary diffSymbol - -ln :: Expr -> Expr -ln = unary lnSymbol - -lambda :: Expr -> Expr -> Expr -lambda = binary lambdaSymbol - -fcomp :: Expr -> Expr -> Expr -fcomp = binary fcompSymbol - ------------------------------------------------------------------ --- Rules for Diffs - -ruleSine :: Rule Expr -ruleSine = rule "Sine" $ - \x -> diff (lambda x (sin x)) :~> lambda x (cos x) - -ruleLog :: Rule Expr -ruleLog = rule "Logarithmic" $ - \x -> diff (lambda x (ln x)) :~> lambda x (1/x) - -ruleDerivPlus :: Rule Expr -ruleDerivPlus = rule "Sum" $ - \x f g -> diff (lambda x (f + g)) :~> diff (lambda x f) + diff (lambda x g) - -ruleDerivMin :: Rule Expr -ruleDerivMin = rule "Sum" $ - \x f g -> diff (lambda x (f - g)) :~> diff (lambda x f) - diff (lambda x g) - -ruleDerivVar :: Rule Expr -ruleDerivVar = rule "Var" $ - \x -> diff (lambda x x) :~> 1 - -ruleDerivProduct :: Rule Expr -ruleDerivProduct = rule "Product" $ - \x f g -> diff (lambda x (f * g)) :~> f*diff (lambda x g) + g*diff (lambda x f) - -ruleDerivQuotient :: Rule Expr -ruleDerivQuotient = rule "Quotient" $ - \x f g -> diff (lambda x (f/g)) :~> (g*diff (lambda x f) - f*diff (lambda x g)) / (g^2) - -{- ruleDerivChain :: Rule Expr -ruleDerivChain = rule "Chain Rule" f - where f (Diff x (f :.: g)) = return $ (Diff x f :.: g) :*: Diff x g - f _ = Nothing -} - ------------------------------------ --- Special rules (not defined with unification) - -ruleDerivCon :: Rule Expr -ruleDerivCon = makeSimpleRule "Constant Term" f - where - f (Sym d [Sym l [Var v, e]]) - | d == diffSymbol && l == lambdaSymbol && v `notElem` collectVars e = return 0 - f _ = Nothing - -ruleDerivMultiple :: Rule Expr -ruleDerivMultiple = makeSimpleRule "Constant Multiple" f - where - f (Sym d [Sym l [x@(Var v), n :*: e]]) - | d == diffSymbol && l == lambdaSymbol && v `notElem` collectVars n = - return $ n * diff (lambda x e) - f (Sym d [Sym l [x@(Var v), e :*: n]]) - | d == diffSymbol && l == lambdaSymbol && v `notElem` collectVars n = - return $ n * diff (lambda x e) - f _ = Nothing - -ruleDerivPower :: Rule Expr -ruleDerivPower = makeSimpleRule "Power" f - where - f (Sym d [Sym l [x@(Var v), Sym p [x1, n]]]) - | d == diffSymbol && l == lambdaSymbol && p == powerSymbol && x==x1 && v `notElem` collectVars n = - return $ n * (x ^ (n-1)) - f _ = Nothing - -ruleDerivChainPowerExprs :: Rule Expr -ruleDerivChainPowerExprs = makeSimpleRule "Chain Rule for Power Exprs" f - where - f (Sym d [Sym l [x@(Var v), Sym p [g, n]]]) - | d == diffSymbol && l == lambdaSymbol && p == powerSymbol && v `notElem` collectVars n = - return $ n * (g ^ (n-1)) * diff (lambda x g) - f _ = Nothing
+ src/Domain/Math/Equation/BalanceRules.hs view
@@ -0,0 +1,37 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Domain.Math.Equation.BalanceRules + ( plusT, minusT, timesT, divisionT + ) where++import Common.Transformation+import Common.View+import Control.Monad+import Domain.Math.Data.Relation+import Domain.Math.Numeric.Views+import Domain.Math.Expr++plusT, minusT :: Functor f => Expr -> Transformation (f Expr)+plusT e = makeTrans $ return . fmap (:+: e)+minusT e = makeTrans $ return . fmap (:-: e)++timesT :: Functor f => Expr -> Transformation (f Expr)+timesT e = makeTrans $ unlessZero e . fmap (e :*:)++divisionT :: Expr -> Transformation (Equation Expr)+divisionT e = makeTrans $ unlessZero e . fmap (:/: e)++unlessZero :: MonadPlus m => Expr -> a -> m a+unlessZero e a = do+ r <- matchM rationalView e+ guard (r /= 0)+ return a
src/Domain/Math/Equation/CoverUpExercise.hs view
@@ -9,22 +9,19 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Domain.Math.Equation.CoverUpExercise (coverUpExercise) where+module Domain.Math.Equation.CoverUpExercise + ( coverUpExercise, coverUpStrategy + ) where import Common.Context import Common.Exercise import Common.Strategy hiding (replicate)-import Common.Transformation-import Common.Uniplate (transform)-import Common.View import Control.Monad-import Data.Ratio import Domain.Math.Data.Relation import Domain.Math.Data.OrList import Domain.Math.Equation.CoverUpRules import Domain.Math.Equation.Views import Domain.Math.Examples.DWO1-import Domain.Math.Numeric.Views import Domain.Math.Expr import Prelude hiding (repeat) @@ -33,14 +30,15 @@ coverUpExercise :: Exercise (OrList (Equation Expr)) coverUpExercise = makeExercise - { description = "solve an equation by covering up"- , exerciseCode = makeCode "math" "coverup"+ { exerciseId = describe "solve an equation by covering up" $+ newId "algebra.equations.coverup" , status = Provisional , parser = parseExprWith (pOrList (pEquation pExpr))- , equivalence = \_ _ -> True+ -- , equivalence = \_ _ -> True , isReady = solvedEquations- , extraRules = map liftToContext coverUpRulesOr+ , extraRules = coverUpRulesOr , strategy = coverUpStrategy+ , navigation = termNavigator , examples = map (orList . return) (concat (fillInResult ++ coverUpEquations)) } @@ -49,8 +47,9 @@ coverUpStrategy :: LabeledStrategy (Context (OrList (Equation Expr))) coverUpStrategy = label "Cover-up" $ - repeat (alternatives $ map (liftToContext . cleanUp) coverUpRulesOr)+ repeat $ somewhere $ alternatives coverUpRulesOr +{- cleanUp :: Rule (OrList (Equation Expr)) -> Rule (OrList (Equation Expr)) cleanUp = doAfter $ fmap $ fmap cleanUpExpr @@ -59,7 +58,7 @@ where f (Negate a) = liftM negate (f a) f (Sqrt a) = match rationalView a >>= rootedRational 2- f (Sym s [Nat n, a]) | s == rootSymbol =+ f (Sym s [Nat n, a]) | isRootSymbol s = match rationalView a >>= rootedRational n f e = match rationalView e @@ -76,7 +75,7 @@ x <- rootedInt a (numerator r) y <- rootedInt a (denominator r) return (fromInteger x / fromInteger y)-+-} ------------------------------------------------------------ -- Testing
src/Domain/Math/Equation/CoverUpRules.hs view
@@ -11,12 +11,13 @@ ----------------------------------------------------------------------------- module Domain.Math.Equation.CoverUpRules ( coverUpRules, coverUpRulesOr+ , coverUp, coverUpOrs , coverUpPower, coverUpPlus, coverUpMinusLeft, coverUpMinusRight , coverUpTimes, coverUpNegate , coverUpNumerator, coverUpDenominator, coverUpSqrt -- parameterized rules , ConfigCoverUp, configName, predicateCovered, predicateCombined- , coverLHS, coverRHS, configCoverUp, varConfig+ , coverLHS, coverRHS, configCoverUp , coverUpPowerWith, coverUpTimesWith, coverUpNegateWith , coverUpPlusWith, coverUpMinusLeftWith, coverUpMinusRightWith , coverUpNumeratorWith, coverUpDenominatorWith, coverUpSqrtWith@@ -24,86 +25,89 @@ , coverUpBinaryRule, commOp, flipOp ) where +import Common.Classes+import Common.Context+import Common.Id+import Common.Rewriting+import Common.Transformation import Common.View-import Domain.Math.Expr-import Domain.Math.Data.Relation import Control.Monad.Identity-import Common.Transformation+import Data.Maybe import Domain.Math.Data.OrList-import Common.Traversable+import Domain.Math.Data.Relation+import Domain.Math.Expr --------------------------------------------------------------------- -- Constructors for cover-up rules -coverUpBinary2Rule :: (OnceJoin f, Switch f, Relational r) - => String -> (Expr -> [(Expr, Expr)]) +coverUpFunction :: (Switch f, Relational r) + => (Expr -> [(Expr, Expr)]) -> (Expr -> Expr -> [f Expr])- -> ConfigCoverUp -> Rule (f (r Expr))-coverUpBinary2Rule opName fm fb cfg = - makeSimpleRuleList name $ onceJoinM $ \eq -> - (guard (coverLHS cfg) >> coverLeft eq) ++ - (guard (coverRHS cfg) >> coverRight eq)+ -> ConfigCoverUp -> r Expr -> [f (r Expr)]+coverUpFunction fm fb cfg eq0 = + (guard (coverLHS cfg) >> coverLeft eq0) ++ + (guard (coverRHS cfg) >> coverRight eq0) where- name = coverUpRuleName opName (configName cfg)- coverRight = map (fmap flipSides) . coverLeft . flipSides- + coverRight = map (fmap flipSides) . coverLeft . flipSides coverLeft eq = do (e1, e2) <- fm (leftHandSide eq) guard (predicateCovered cfg e1) new <- fb (rightHandSide eq) e2- switch $ fmap (guard . predicateCombined cfg) new+ _ <- switch $ fmap (guard . predicateCombined cfg) new return (fmap (constructor eq e1) new) +coverUpBinaryOrRule :: Relational r+ => String -> (Expr -> [(Expr, Expr)]) + -> (Expr -> Expr -> [OrList Expr])+ -> ConfigCoverUp -> Rule (OrList (r Expr))+coverUpBinaryOrRule opName fm fb cfg =+ let name = coverUpRuleName opName (configName cfg)+ in makeSimpleRuleList name $ oneDisjunct $ coverUpFunction fm fb cfg+ coverUpBinaryRule :: Relational r => String -> (Expr -> [(Expr, Expr)]) -> (Expr -> Expr -> Expr) -> ConfigCoverUp -> Rule (r Expr)-coverUpBinaryRule opName fm fb =- let v = makeView (return . Identity) runIdentity- fbi x y = [Identity (fb x y)]- in liftRule v . coverUpBinary2Rule opName fm fbi+coverUpBinaryRule opName fm fb cfg = + let name = coverUpRuleName opName (configName cfg)+ fb2 a b = [Identity (fb a b)]+ in makeSimpleRuleList name $ map runIdentity . coverUpFunction fm fb2 cfg coverUpUnaryRule :: Relational r => String -> (Expr -> [Expr]) -> (Expr -> Expr) -> ConfigCoverUp -> Rule (r Expr) coverUpUnaryRule opName fm fb = coverUpBinaryRule opName (map (\e -> (e, e)) . fm) (const . fb) -coverUpRuleName :: String -> Maybe String -> String-coverUpRuleName opName viewName =- "cover-up " ++ opName ++ maybe "" (\s -> " [" ++ s ++ "]") viewName+coverUpRuleName :: String -> String -> Id+coverUpRuleName opName cfg =+ let f = if null cfg then newId else ( cfg # )+ in "algebra.equations.coverup" # f opName --------------------------------------------------------------------- -- Configuration for cover-up rules data ConfigCoverUp = Config- { configName :: Maybe String+ { configName :: String , predicateCovered :: Expr -> Bool , predicateCombined :: Expr -> Bool , coverLHS :: Bool , coverRHS :: Bool } +-- Default configuration: cover-up part with variables configCoverUp :: ConfigCoverUp configCoverUp = Config- { configName = Nothing- , predicateCovered = const True- , predicateCombined = const True+ { configName = ""+ , predicateCovered = hasSomeVar+ , predicateCombined = hasNoVar , coverLHS = True , coverRHS = True } --- default configuration-varConfig :: ConfigCoverUp -varConfig = configCoverUp- { configName = Just "vars"- , predicateCovered = hasVars- , predicateCombined = noVars- }- --------------------------------------------------------------------- -- Parameterized cover-up rules coverUpPowerWith :: ConfigCoverUp -> Rule (OrList (Equation Expr))-coverUpPowerWith = coverUpBinary2Rule "power" (isBinary powerSymbol) fb+coverUpPowerWith = coverUpBinaryOrRule "power" (isBinary powerSymbol) fb where fb rhs e2 = do n <- isNat e2@@ -116,10 +120,10 @@ coverUpPlusWith = coverUpBinaryRule "plus" (commOp . isPlus) (-) coverUpMinusLeftWith :: ConfigCoverUp -> Rule (Equation Expr)-coverUpMinusLeftWith = coverUpBinaryRule "minus left" isMinus (+)+coverUpMinusLeftWith = coverUpBinaryRule "minus-left" isMinus (+) coverUpMinusRightWith :: ConfigCoverUp -> Rule (Equation Expr)-coverUpMinusRightWith = coverUpBinaryRule "minus right" (flipOp . isMinus) (flip (-))+coverUpMinusRightWith = coverUpBinaryRule "minus-right" (flipOp . isMinus) (flip (-)) -- | Negations are pushed inside coverUpTimesWith :: ConfigCoverUp -> Rule (Equation Expr)@@ -140,7 +144,7 @@ coverUpDenominatorWith = coverUpBinaryRule "denominator" (flipOp . matchM divView) (flip (/)) coverUpSqrtWith :: ConfigCoverUp -> Rule (Equation Expr)-coverUpSqrtWith = coverUpUnaryRule "square root" isSqrt (\x -> x*x)+coverUpSqrtWith = coverUpUnaryRule "sqrt" isSqrt (\x -> x*x) where isSqrt (Sqrt a) = return a isSqrt _ = []@@ -148,9 +152,23 @@ --------------------------------------------------------------------- -- Cover-up rules for variables -coverUpRulesOr :: [Rule (OrList (Equation Expr))]-coverUpRulesOr = coverUpPower : map ruleOnce coverUpRules+coverUpOrs :: OrList (Equation Expr) -> OrList (Equation Expr)+coverUpOrs = join . fmap (f . coverUp)+ where+ f :: Equation Expr -> OrList (Equation Expr)+ f eq = case apply coverUpPower (return eq) of+ Just xs -> coverUpOrs xs+ Nothing -> return eq+ +coverUp :: Equation Expr -> Equation Expr+coverUp eq = + case mapMaybe (`apply` eq) coverUpRules of+ hd:_ -> coverUp hd+ _ -> eq +coverUpRulesOr :: IsTerm a => [Rule (Context a)]+coverUpRulesOr = use coverUpPower : map use coverUpRules+ coverUpRules :: [Rule (Equation Expr)] coverUpRules = [ coverUpPlus, coverUpMinusLeft, coverUpMinusRight, coverUpNegate@@ -161,15 +179,15 @@ coverUpPlus, coverUpMinusLeft, coverUpMinusRight, coverUpTimes, coverUpNegate, coverUpNumerator, coverUpDenominator, coverUpSqrt :: Rule (Equation Expr) -coverUpPower = coverUpPowerWith varConfig-coverUpPlus = coverUpPlusWith varConfig-coverUpMinusLeft = coverUpMinusLeftWith varConfig-coverUpMinusRight = coverUpMinusRightWith varConfig-coverUpTimes = coverUpTimesWith varConfig-coverUpNegate = coverUpNegateWith varConfig-coverUpNumerator = coverUpNumeratorWith varConfig-coverUpDenominator = coverUpDenominatorWith varConfig-coverUpSqrt = coverUpSqrtWith varConfig+coverUpPower = coverUpPowerWith configCoverUp+coverUpPlus = coverUpPlusWith configCoverUp+coverUpMinusLeft = coverUpMinusLeftWith configCoverUp+coverUpMinusRight = coverUpMinusRightWith configCoverUp+coverUpTimes = coverUpTimesWith configCoverUp+coverUpNegate = coverUpNegateWith configCoverUp+coverUpNumerator = coverUpNumeratorWith configCoverUp+coverUpDenominator = coverUpDenominatorWith configCoverUp+coverUpSqrt = coverUpSqrtWith configCoverUp --------------------------------------------------------------------- -- Some helper-functions
src/Domain/Math/Equation/Views.hs view
@@ -19,7 +19,7 @@ import Domain.Math.Data.OrList import Domain.Math.Data.Relation import Common.View-import Common.Traversable+import Common.Classes -- generalized to relation solvedRelations :: (Crush f, Relational g) => f (g Expr) -> Bool@@ -31,9 +31,9 @@ solvedRelation r = case (getVariable (leftHandSide r), getVariable (rightHandSide r)) of (Just _, Just _) -> False- (Just x, Nothing) -> x `notElem` collectVars (rightHandSide r)- (Nothing, Just x) -> x `notElem` collectVars (leftHandSide r)- _ -> noVars (leftHandSide r) && noVars (rightHandSide r)+ (Just x, Nothing) -> withoutVar x (rightHandSide r)+ (Nothing, Just x) -> withoutVar x (leftHandSide r)+ _ -> hasNoVar (leftHandSide r) && hasNoVar (rightHandSide r) -- The variable must appear on the left solvedRelationWith :: Relational f => (Expr -> Bool) -> f Expr -> Bool@@ -48,12 +48,12 @@ solvedEquation :: Equation Expr -> Bool solvedEquation eq@(lhs :==: rhs) = - (eq `belongsTo` equationSolvedForm) || (noVars lhs && noVars rhs)+ (eq `belongsTo` equationSolvedForm) || (hasNoVar lhs && hasNoVar rhs) equationSolvedForm :: View (Equation Expr) (String, Expr) equationSolvedForm = makeView f g where- f (Var x :==: e) | x `notElem` collectVars e =+ f (Var x :==: e) | withoutVar x e = return (x, e) f _ = Nothing g (s, e) = Var s :==: e
src/Domain/Math/Examples/DWO1.hs view
@@ -20,6 +20,7 @@ ) where import Prelude hiding ((^))+import Common.Rewriting import Domain.Math.Data.Relation import Domain.Math.Expr
src/Domain/Math/Examples/DWO3.hs view
@@ -15,6 +15,7 @@ module Domain.Math.Examples.DWO3 where import Prelude hiding ((^))+import Common.Rewriting import Domain.Math.Expr ----------------------------------------------------------
+ src/Domain/Math/Examples/DWO4.hs view
@@ -0,0 +1,502 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-- Example exercises from the Digital Mathematics Environment (DWO),+-- see: http://www.fi.uu.nl/dwo/gr/frameset.html.+--+-----------------------------------------------------------------------------+module Domain.Math.Examples.DWO4 + ( brokenEquations, normBroken, normBroken2, normBrokenCon, deelUit+ , powerEquations, expEquations, logEquations, higherPowerEquations+ , rootEquations, rootEquations2, rootSubstEquations, expEquations2+ ) where++import Prelude hiding ((^))+import Common.Rewriting+import Domain.Math.Data.Relation+import Domain.Math.Expr++----------------------------------------------------------+-- HAVO B applets++-- Hoofdstuk 7, vergelijkingen met machten algebraisch (6)+powerEquations :: [[Equation Expr]]+powerEquations = + -- los vergelijkingen algebraisch op+ let x = Var "x" in+ [ [ x^14 :==: 25+ , x^(-7) :==: 110+ , 2*x^(3.5) :==: 70+ , 8*x^(-(9.2)) :==: 1000+ ]+ , [ root x 5 :==: 2.9+ , 5 * root x 3 :==: 7+ , root (x^3) 4 :==: 720+ , root (x^2) 5 :==: 5.5+ ]+ , [ 4*x^(-12) :==: 28 + , 7*x^(5.1) + 16 :==: 100+ , 8*x^(-((1.9))) - 5 :==: 2+ , 0.8 * x^(0.7) + 7 :==: 12.5+ ]+ , [ 4*root x 7 + 7 :==: 11.8+ , 9*x^(3.2)+17 :==: 37+ , 6*x^(-(3.1))-9 :==: 12+ , 0.7 * x^(-(1.1)) + 17 :==: 40+ ]+ ]++-- Hoofdstuk 7, exponentiele vergelijkingen algebraisch (7)+expEquations :: [[Equation Expr]]+expEquations =+ -- los exponentiele vergelijkingen algebraisch op+ let x = Var "x" in+ [ [ 2^x :==: 16 * sqrt 2+ , 2^(x+2) :==: 1/4+ , 3^(x-1) :==: 81+ , 3^(x+5) :==: 243/(sqrt 3)+ ]+ , [ 5^(2-x) :==: 0.04+ , 3^(2*x) :==: 1/9+ , 3^(1-3*x) :==: 81+ , 3^(3*x-2) :==: 3*sqrt 3+ ]+ , [ 5*2^(x-1) :==: 20*sqrt 2+ , 6*5^(2-x) :==: 150+ , 2*7^(4*x-1) :==: 98+ , 8*3^(5-2*x) :==: 72*sqrt 3+ ]+ , [ 2^x-7 :==: 9+ , 4^(3*x)+5 :==: 69+ , 7*3^(2*x+1) :==: 189+ , 5*2^(1-4*x)+11 :==: 51+ ]+ , [ 5^(x-4) :==: (1/5)^(2*x+1)+ , 7^(1-2*x) :==: 1+ , 4^(2*x-3) :==: 2*sqrt 2+ , 2*9^(1-2*x) :==: 6*sqrt 3+ ]+ ]++-- Hoofdstuk 7, logaritmische vergelijkingen algebraisch (8)+logEquations :: [[Equation Expr]]+logEquations =+ -- los algebraisch op+ let x = Var "x" in+ [ [ logBase 2 x :==: 7+ , logBase 3 (x-2) :==: 2+ , logBase 4 (x-3) :==: 1+(1/2)+ , logBase 5 ((1/10)*x-3) :==: -1+ , logBase x 7 :==: 1+ , logBase x 4 :==: -1+ , logBase 2 (x^2-1) :==: 3+ , logBase (1/3) (1-5*x) :==: -1+ ]+ ]+++----------------------------------------------------------+-- VWO A/C applets++-- Hoofdstuk 5, hogeremachtswortels (1)+higherPowerEquations :: [[Equation Expr]]+higherPowerEquations =+ -- bereken exacte oplossing+ let x = Var "x" in+ [ [ 2*x^3+9 :==: 19+ , 4*x^5-17 :==: 27+ , 3*x^7+8 :==: 62+ , 5*x^3-1 :==: 9+ , 6-5*x^3 :==: 76+ , 11-7*x^5 :==: 53+ , 4-(1/5)*x^7 :==: 9+ , 18-11*x^7 :==: 62+ ]+ , [ (1/2)*x^4+5 :==: 12+ , 5*x^6-37 :==: 68+ , 4*x^8-19 :==: 9+ , 5*x^6+7 :==: 97+ , 18-7*x^4 :==: -38+ , 3+(1/3)*x^6 :==: 7+ , 1-(1/9)*x^8 :==: -4+ , 47+15*x^8 :==: 77+ ]+ , [ 18*x^8-11 :==: 7+ , (1/4)*x^6+14 :==: 30+ , 5*x^4+67 :==: 472+ , 5*x^4-1 :==: 4+ , (1/8)*x^7+24 :==: 40+ , (1/5)*x^3+27 :==: 52+ , 32*x^3+18 :==: 22+ , 4*x^3-8 :==: 100+ ]+ , [ 14-2*x^3 :==: 700+ , 4-3*x^5 :==: 100+ , 14-11*x^7 :==: 25+ , 1-3*x^5 :==: 97+ ]+ -- Geef in twee decimalen nauwkeurig+ , [ 3*x^5+7 :==: 15+ , 0.7 * x^4 - 1.3 :==: 2+ , (1/3)*x^7 :==: 720+ ]+ ]++-- Hoofdstuk 5, hogeremachtswortels (2)+rootEquations :: [[Equation Expr]]+rootEquations = + -- Bereken exacte oplossing+ let x = Var "x" in+ let y = Var "y" in+ [ [ x^4 :==: 6+ , root x 4 :==: 6+ , sqrt x :==: 10+ , root x 5 :==: 2+ ]+ , [ 3*x^5-1 :==: 20+ , 3*root (x-1) 5 - 1 :==: 20+ , (1/10)*sqrt x + 2 :==: 12+ , (1/5)*x^7+8 :==: 26+ ]+ , [ 3*root x 4+2 :==: 14+ , (1/2)*x^8-2 :==: 18+ , 5-2*root x 3 :==: 3+ ]+ -- Maak x vrij+ , [ y :==: x^5+ , y :==: 2*x^5+4+ , y :==: (1/10)*x^3-6+ , y :==: root x 7+ , y :==: 2*root x 3+8+ , y :==: (1/10)*root x 5-6+ ]+ , [ y :==: 3*root x 7-6+ , y :==: (1/4)*x^9-6+ , y :==: 8+(1/2)*root x 3+ ]+ ]++++----------------------------------------------------------+-- VWO B applets++-- Hoofdstuk 1, wortelvergelijkingen+rootEquations2 :: [[Equation Expr]]+rootEquations2 =+ let x = Var "x" in+ -- los algebraisch op+ [ [ 5-2*sqrt x :==: 1+ , 7-3*sqrt x :==: 5+ , 4-2*sqrt x :==: -3+ , 6-3*sqrt x :==: 2+ ]+ , [ 2*sqrt x :==: x+ , 2*sqrt x :==: 3*x+ , x-3*sqrt x :==: 0+ , 3*x-5*sqrt x :==: 0+ ]+ , [ x :==: sqrt (2*x+3)+ , x :==: sqrt (3*x+10)+ , x :==: sqrt (4*x+21)+ , x :==: sqrt (3*x+4)+ ]+ , [ 5*x :==: sqrt (50*x+75)+ , 2*x :==: sqrt (24*x+28)+ , 3*x :==: sqrt (27*x-18)+ , 2*x :==: sqrt (28*x-40)+ , 3*x :==: sqrt (3*x+42)+ , 5*x :==: sqrt (49*x+2)+ , 3*x :==: sqrt (10*x-1)+ , 5*x :==: sqrt (30*x-5)+ ]+ , [ x-sqrt x :==: 6+ , x-4*sqrt x :==: 12+ , x-sqrt x :==: 12+ , x-sqrt x :==: 2+ , 2*x+sqrt x :==: 3+ , 3*x+4*sqrt x :==: 20+ , 2*x+sqrt x :==: 15+ , 2*x-3*sqrt x :==: 27+ ]+ ]++-- Hoofdstuk 1, wortelvergelijkingen+rootSubstEquations :: [[Equation Expr]]+rootSubstEquations =+ let x = Var "x" in+ -- los algebraisch op+ [ [ 8*x^3+1 :==: 9*x*sqrt x+ , 27*x^3 :==: 28*x*sqrt x-1+ , x^3+3 :==: 4*x*sqrt x+ , x^3 :==: 10*x*sqrt x-16+ ]+ , [ x^3 :==: 6*x*sqrt x+16+ , x^3-24*x*sqrt x :==: 81+ , x^3+x*sqrt x :==: 20+ , x^3-15 :==: 2*x*sqrt x+ ]+ , [ x^5+32 :==: 33*x^2*sqrt x+ , 243*x^5-244*x^2*sqrt x+1 :==: 0+ , 32*x^5+31*x^2*sqrt x :==: 1+ , x^5 :==: 242*x^2*sqrt x+243+ ]+ , [ x^5+8 :==: 6*x^2*sqrt x+ , x^5 :==: 9*x^2*sqrt x-18+ , x^5 :==: 5*x^2*sqrt x+24+ , x^5+4*x^2*sqrt x :==:12+ ]+ ]++-- Hoofdstuk 1, gebroken vergelijkingen+brokenEquations :: [[Equation Expr]]+brokenEquations =+ -- Bereken exact de oplossingen+ let x = Var "x" in+ [ [ (2*x^2-10) / (x^2+3) :==: 0+ , (7*x^2-21) / (2*x^2-5) :==: 0+ , (3*x^2-6) / (4*x^2+1) :==: 0+ , (4*x^2-24) / (6*x^2-2) :==: 0+ , x^2 / (x+4) :==: (3*x+4) / (x+4)+ , (x^2+2) / (x-2) :==: (x+8) / (x-2)+ , (x^2+6*x-6)/(x^2-1) :==: (4*x+9)/(x^2-1)+ , (x^2+6)/(x^2-2) :==: (7*x)/(x^2-2)+ ]+ , [ (x^2+6*x)/(x^2-1) :==: (3*x+4)/(x^2-1)+ , (x^2+6)/(x-3) :==: (5*x)/(x-3) + , (x^2+4*x)/(x^2-4) :==: (3*x + 6)/(x^2-4)+ , (x^2+2*x-4)/(x-5) :==: (4*x+11)/(x-5)+ , (5*x+2)/(2*x-1) :==: (5*x+2)/(3*x+5)+ , (x^2-9)/(4*x-1) :==: (x^2-9)/(2*x+7)+ , (3*x-2)/(2*x^2) :==: (3*x-2)/(x^2+4)+ , (2*x+1)/(x^2+3*x) :==: (2*x+1)/(5*x+8)+ ]+ , [ (x^2-1)/(2*x+2) :==: (x^2-1)/(x+8)+ , (x^2-4)/(3*x-6) :==: (x^2-4)/(2*x+1)+ , (x^2+5*x)/(2*x^2) :==: (x^2+5*x)/(x^2+4)+ , (x^2-3*x)/(2*x-6) :==: (x^2-3*x)/(4*x+2)+ , x/(x+1) :==: 1 + 3/4+ , (x+2)/(3*x) :==: 1 + 1/3+ , (2*x+3)/(x-1) :==: 3 + 1/2+ , (x-3)/(1-x) :==: 1 + 2/5+ ]+ , [ (x+4)/(x+3) :==: (x+1)/(x+2)+ , (2*x+3)/(x-1) :==: (2*x-1) / (x-2)+ , (3*x+6)/(3*x-1) :==: (x+4)/(x+1)+ , (x+2)/(2*x+5) :==: (x+4)/(2*x-3)+ , (x+5)/(2*x) + 2 :==: 5+ , (3*x+4)/(x+2) - 3 :==: 2+ , (x^2)/(5*x+6) + 4 :==: 5+ , (x^2)/(2*x-3) + 3 :==: 7+ ]+ , [ (x-2)/(x-3) :==: x/2+ , (x+9)/(x-5) :==: 2/x+ , (x+2)/(x+4) :==: 2/(x+1)+ , (-3)/(x-5) :==: (x+3)/(x+1)+ , (x+1)/(x+2) :==: (7*x+1)/(2*x-4)+ , (2*x-7)/(5-x) :==: (x+1)/(3*x-7)+ , (x+1)/(x-1) :==: (3*x-7)/(x-2)+ , (3*x-7)/(x-2) :==: (7-x)/(3*x-3)+ ]+ ]+ +-- Hoofdstuk 4, gebroken vorm herleiden (1 en 1a)+normBroken :: [[Expr]]+normBroken =+ -- Herleid+ let x = Var "x" in+ let y = Var "y" in+ let a = Var "a" in+ let b = Var "b" in+ [ [ 7/(2*x) + 3/(5*x), 3/(2*x) + 2/(3*x), 4/(5*x)-2/(3*x)+ , 2/(7*x) - 1/(4*x), 5/(6*a)+3/(7*a), 3/(8*a)+5/(3*a)+ , 7/(2*a)-2/(3*a), 9/(5*a)-1/(2*a)+ ]+ , [ 1/x+1/y, 2/(3*x)+1/(2*y), 3/(x^2*y) - 5/(2*x*y), 2/(x*y)-7/(5*y)+ , 2/a - 3/b, 4/(3*a)-2/(5*b), 2/(a*b)+4/(3*a), 7/(4*a)+3/(4*b)+ ]+ , [ 3+1/(2*x), 2*x+(3/(5*x)), 5/(2*x)-3, 3-5/(7*x), 5/(3*a)+1+ , 4*a+3/(2*a), 2*a-1/(3*a), 7/(5*a)-2+ ]+ , [ 5/(x+2)+4/(x+3), 3/(x-1)+2/(x+3), 4/(x+5)+2/(x-3), 3/(x-2)+2/(x-3)+ , 4/(x+3)-6/(x+2), 1/(x+5)-3/(x-4), 7/(x-3)-2/(x+1), 6/(x-1)-3/(x-2)+ ]+ , [ (x+1)/(x+2)+(x+2)/(x-3), (x-2)/(x+3)+(x-1)/(x+2), (x+3)/(x-1)+(x+2)/(x-4)+ , (x-4)/(x+5)+(x-2)/(x-3), (x-1)/(x+1)-(x+2)/(x-2), (x+5)/(x+3)-(x+3)/(x+5)+ , (x-1)/(x+2)-(x+4)/(x+1), (x-3)/(x-1)-(x+2)/(x+4)+ ]+ , [ (2*x)/(x-1)+x/(x+2), (3*x)/(x-4)+(5*x)/(x-2)+ , (4*x)/(x+2)-(2*x)/(x+1), x/(x+5)-(4*x)/(x+6)+ ]+ ]++-- Hoofdstuk 4, gebroken vorm herleiden (2 en 2a)+normBroken2 :: [[Expr]]+normBroken2 =+ -- Herleid+ let x = Var "x" in+ let a = Var "a" in+ let p = Var "p" in+ [ [ (x^2+4*x-5)/(x^2+5*x-6), (x^2+2*x-8)/(x^2+10*x+24)+ , (x^2-7*x+12)/(x^2+x-20), (x^2+7*x+12)/(x^2+5*x+6)+ , (a^2-a-2)/(a^2+4*a-12), (a^2-3*a-10)/(a^2-a-20)+ , (a^2-2*a-15)/(a^2-3*a-18), (a^2+a-2)/(a^2+3*a+2)+ ]+ , [ (x^2-16)/(x^2+x-12), (x^2-2*x+1)/(x^2-1), (x^2-9)/(x^2+6*x+9)+ , (x^2-7*x+6)/(x^2-1), (2*p^2+8*p)/(p^2-16), (-(p^2)+5*p)/(p^2-10*p+25)+ , (p^2-4)/(4*p^2+8*p), (p^2-12*p+36)/(p^2-6*p)+ ]+ , [ (x^3+3*x^2+2*x)/(x^2+4*x+4), (x^3+10*x^2+24*x)/(x^2+7*x+6)+ , (x^2+5*x+6)/(x^3-x^2-6*x), (x^2+3*x-4)/(x^3-6*x^2+5*x)+ , (a^3+7*a^2+12*a)/(a^2+6*a+9), (a^3+7*a^2+10*a)/(a^2-a-6)+ , (a^2-9)/(a^3-4*a^2+3*a), (a^2-2*a-15)/(a^3-3*a^2-10*a)+ ]+ ]+ +deelUit :: [[Expr]]+deelUit =+ let x = Var "x" in+ let a = Var "a" in+ let p = Var "p" in+ let t = Var "t" in+ [ -- laatste sommen van gebroken vorm herleiden (2), niveau 5+ [ (-6*a^2-1)/a, -2*p^2+3/(7*p), (7*t^2+4)/(-4*t), (9*x^2+8)/(8*x)+ ]+ , -- sommen (2a)+ [ (-7*a^2-4*a-6)/(-6*a), (3*p^2+6*p-8)/p, (2*t^2-9*t-8)/(-2*t)+ , (x^2+5*x+5)/(2*x), (5*a^3-4*a+2)/(9*a), (5*p^3-7*p^2+9)/(2*p)+ , (-3*t^3+6*t-4)/(3*t), (4*x^3-3*x^2+4)/(7*x)+ ]+ ]+ +-- Vervolg hoofdstuk 4, gebroken vorm herleiden (2 en 2a), vanaf niveau 4+normBrokenCon :: [[Equation Expr]]+normBrokenCon =+ -- Herleid+ let a = Var "a" in+ let p = Var "p" in+ let t = Var "t" in+ let ca = symbol (newSymbol "A") in+ let ct = symbol (newSymbol "T") in+ let cn = symbol (newSymbol "N") in+ [ [ ca :==: (p^2+2*p)/(p^2-4), ca :==: (6*p^2-18*p)/(p^2-9)+ , ca :==: (p^2-1)/(-2*p^2+2*p), ca :==: (p^2-16)/(4*p^2+16*p)+ , ct :==: (t^3-2*t^2)/(t^2-4), ct :==: (t^3+4*t^2)/(t^2-16)+ , ct :==: (t^2-1)/(t^3+t^2), ct :==: (t^2-25)/(t^3-5*t^2)+ ]+ , [ cn :==: (a^4+4*a^2-5)/(a^4-1), cn :==: (a^4+5*a^2+6)/(a^4+4*a^2+3)+ , cn :==: (a^4-5*a^2+6)/(a^4-7*a^2+10), cn :==: (a^4-8*a^2+16)/(a^4-5*a^2+4)+ ]+ ]++-- Hoofdstuk 5, exponentiele vergelijkingen exact oplossen (1, 2, 2a)+expEquations2 :: [[Equation Expr]]+expEquations2 =+ let x = Var "x" in+ -- los algebraisch op+ -- 1+ [ [ 2^(2*x-1) :==: 1/16+ , 3^(1-x) :==: 81+ , 5^(1-2*x) :==: 1/5+ , (1/2)^(4*x-3) :==: 1/4+ , (1/3)^(5*x+2) :==: 1/3+ , 6^(3*x-2) :==: 1/216+ ]+ , [ 2^(3*x+2) :==: 2*sqrt 2+ , 3^(2*x+1) :==: 9*sqrt 3+ , 5^(4*x+3) :==: 625*sqrt 5+ , (1/2)^(x+1) :==: 4+ , (1/3)^(x-3) :==: 3+ , 4^(x+2) :==: 64*root 4 3+ ]+ , [ 2^(x+3) :==: (1/2)*root 2 3+ , 3^(4*x+1) :==: 27+ , 5^(-x+2) :==: 1/25+ , (1/2)^(1-x) :==: sqrt 2+ , (1/3)^(x+1) :==: (1/9)*sqrt 3+ , 2^(1-3*x) :==: (1/8)*sqrt 2+ ]+ , [ 3*2^x+1 :==: 25+ , 4*3^x-9 :==: 27+ , 2*5^x+4 :==: 14+ , 5*(1/2)^x+11 :==: 51+ , 8*(1/3)^x+27 :==: 99+ , 3*(1/5)^x-35 :==: 40+ ]+ , [ 2^(4*x+3) :==: 1+ , (1/2)^(2*x-1) :==: 1+ , 3^(2*x+4) :==: 1+ , (1/3)^(x-3) :==: 1+ , 4^(4*x-7) :==: 1+ , 5^(3*x-6) :==: 1+ ]+ -- 2+ , [ 2^(2*x+1) :==: (1/2)^(x+2)+ , 4^(2*x-1) :==: 2^(3*x+2)+ , 2^(5*x-4) :==: 8^(x-3)+ , (1/4)^(2*x+1) :==: 2^(6-2*x)+ , (1/3)^(2*x-3) :==: 3^(4*x-3)+ , 3^(3*x-2) :==: 9^(2-x)+ , 27^(2*x+1) :==: 3^(2*x-5)+ , 3^(5*x-1) :==: (1/9)^(2*x-1)+ ]+ , [ 6^(7*x-3) :==: 36^(2*x+3)+ , (1/7)^(2*x-1) :==: 7^(2*x-7)+ , 5^(5-2*x) :==: (1/5)^(x+2)+ , 25^(4*x+1) :==: 5^(5*x-4)+ , 3^(x^2) :==: (1/3)^(2*x)+ , (1/2)^(x^2) :==: 2^(2*x)+ , 5^(x^2) :==: 25^(3*x)+ , 2^(x^2) :==: (1/8)^(-x)+ ]+ , [ (1/2)^(2-2*x) :==: 4^(3*x+5)+ , 8^(x+1) :==: (1/2)^(x+7)+ , (1/4)^(x+2) :==: 8^(2*x-1)+ , 8^(2*x-3) :==: 16^(2*x+3)+ , (1/3)^(x-2) :==: 9^(x+4)+ , 9^(2*x-1) :==: 27^(2*x-1)+ , (1/9)^(x+3) :==: 27^(2*x+2)+ , 27^(3-2*x) :==: (1/3)^(4*x+3)+ ]+ , [ 4*2^x :==: 2^(3*x-2)+ , 2^(5*x-9) :==: (1/8)*2^x+ , 3^(4*x+6) :==: 27*3^x+ , (1/9)*3^x :==: 3^(2-3*x)+ , 3*3^x :==: (1/3)^(2*x+5)+ , 4^(x+1) :==: 8*2^x+ , (1/2)*2^x :==: (1/2)^x+ , 9^(x+2) :==: (1/3)*3^x+ ]+ , [ (1/5)*5^(3*x-2) :==: 25^(x+1)+ , 9*3^(2*x+1) :==: (1/3)^(4*x-3)+ , 4^(3*x-5) :==: 8*2^(x+2)+ , (1/2)^(3-2*x) :==: (1/4)*2^(3*x-4)+ , 2^(x+2)+2^x :==: 40+ , 2^(x+4) :==: 3/4+2^(x+2)+ , 2^(x-2)+2^(x+1) :==: 9+ , 2^(x+5)-2^(x+4) :==: 16+ ]+ -- 2a+ , [ 3^(x+2) :==: 72+3^x+ , 3^(x-1)+3^(x+1) :==: 10+ , 3^(x+3)+3^(x+2) :==: 12+ , 3^x-3^(x-1) :==: 54+ ]+ , [ 5^(x+1)+5^x :==: 150+ , 5^(x+1) :==: 100+5^x+ , 5^(x+2)+5^x :==:1+1/25+ , 5^(x+1)+5^(x+2) :==: 30+ ]+ , [ 2^(x+4)-2^(x-2) :==: 63*sqrt 2+ , 3^(x-1)+3^x :==: 12*sqrt 3+ , 5^x-5^(x-1) :==: 4*sqrt 5+ , 2^(x+2)+2^(x-3) :==: 66*sqrt 2+ ]+ ]
+ src/Domain/Math/Examples/DWO5.hs view
@@ -0,0 +1,167 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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)+--+-- Example exercises from the Digital Mathematics Environment (DWO),+-- see: http://www.fi.uu.nl/dwo/gr/frameset.html.+--+-----------------------------------------------------------------------------+module Domain.Math.Examples.DWO5 + ( diffSet1, diffSet2, diffSet3, diffSet4+ , diffSet5, diffSet6, diffSet7, diffSet8+ ) where++import Domain.Math.Expr+import Prelude hiding ((^))+import Data.Maybe++differentiateLists :: [[Expr]] -> [[Expr]]+differentiateLists = map (map differentiate)++differentiate :: Expr -> Expr+differentiate a = + let x = fromMaybe "x" (selectVar a) + in unary diffSymbol $ binary lambdaSymbol (Var x) a++----------------------------------------------------------+-- HAVO B applets++-- Hoofdstuk 6, differentieer+-- Bereken de afgeleide+diffSet1 :: [[Expr]]+diffSet1 = differentiateLists $+ let x = Var "x" in+ let p = Var "p" in+ let q = Var "q" in+ let r = Var "r" in+ [ [ 3*x^4 - 7*x^2, -x^3-5*x, 1/2*x^6-5*x^2+4, -1/3*x^3+(1+1/2)*x^2-x+1]+ , [ -x^5+5*x+23, -2*p^4+5*p-12, 3/5*q^5-q^3+4*q, -2/3*r^6+1/4*r^4-3*r+7] + , -- werk eerst de haakjes weg+ [ (x-2)^2, -(1-3*x)^2, (x-1)*(2*x+5), -(1-3*x)*(2*x+7)]+ -- differentieer+ , [x^3-x*(x+5), -2*(p+1)*(p-12), q*(q^5-q^3)+3*q^2+4, -3*r*(r-1)*(r+2)]+ ]+ +----------------------------------------------------------+-- VWO A/C applets++-- Hoofdstuk 7, differentieer+diffSet2 :: [[Expr]]+diffSet2 = differentiateLists $+ let x = Var "x" in+ [ [ 5*x^2, -4*x^2, 10*x^2-8, -8*x^2+7]+ , [ 3*x^2+4*x, -0.5*x^2-2*x, -8*x^2+7*x-3, -0.25*x^2+x-1]+ , [ (x+2)^2, (5*x+7)*(4-3*x), (3*x+6)^2-8*x+ , 5*(x-3)^2+5*x, 5*(x-3)^2+5*(2*x-1), -3*(x-1)*(5-9*x)-8*(x-7) ]+ ]+ +-- Hoofdstuk 7, bereken de afgeleide: zelfde als Havo B applet++----------------------------------------------------------+-- VWO B applets++-- Hoofdstuk 3, differentieren: zelfde als Havo B applet++-- Hoofdstuk 7+-- Gebruik de productregel+diffSet3 :: [[Expr]]+diffSet3 = differentiateLists $+ let x = Var "x" in+ [ [ (x^2+2*x)*(3*x+5), (2*x^2-3*x)*(4*x+1), (3*x^3+4*x)*(x^2-2)+ , (4*x^3-x)*(3*x^2+7*x), (x^2+2*x)*(x^3-4*x^2+3), (5*x-7)*(2*x^3-3*x+1)+ , (3*x^2+2)*(5*x^3+4*x^2-7*x), (4*x+1)*(3*x^3-x^2+2*x)+ ]+ , [ (3*x+1)^2, (5*x-2)^2, (2*x+7)^2, (4*x-3)^2+ , (2*x^2-3*x)^2, (3*x^2+2)^2, 2*x^3-3*x^2, (5*x^3+7*x)^2+ ]+ ]+ +-- Gebruik de quotientregel+diffSet4 :: [[Expr]]+diffSet4 = differentiateLists $+ let x = Var "x" in+ [ [ 5/(x-1), 3/(x+2), (-2)/(x-3), (-3)/(x+4), 3/(2*x-1)+ , 2/(3*x+4), (-4)/(3*x-1), (-2)/(4*x+3) + ]+ , [ (x+1)/(x-2), (x-3)/(x+4), (x+5)/(x-1), (x-2)/(x+1)+ , (2*x+3)/(4*x-1), (3*x-1)/(2*x+1), (4*x+3)/(3*x-2), (5*x-2)/(3*x+4)+ ]+ , [ (3*x^2)/(2*x^3+4), (2*x^3)/(3*x^2-1), (x^2)/(4*x^3-2)+ , (3*x^3)/(5*x^2+7), (1-x^3)/(x+4), (x+3)/(2-x^2)+ , (1-2*x^3)/(x+1), (x+5)/(2-3*x^2)+ ]+ , [ (2-x)/(x^2+1)+2*x^3, (x^3-3)/(4-x)+x^2+ , (3-2*x)/(2*x^2-3)+x^3, (2*x^3-4)/(6-5*x)+4*x^2+ ] + ]+ +-- differentieer x^n (n geheel), noteer zonder negatieve exponent+diffSet5 :: [[Expr]]+diffSet5 = differentiateLists $+ let x = Var "x" in+ [ [ 4/x^2, 5/x^3, 2/x^4, 3/x^5, 1/9*x^2, 1/7*x^3, 1/5*x^4, 1/8*x^5 ]+ , [ 3*x^2-4/(x^2), 7*x^3-2/(x^3), 2*x^4-5/(x^4), 2*x^5-6/(x^5) + , (3*x+2)/(x^3), (2*x^2-4)/x^5, (4*x-3)/x^2, (6*x^2+5)/x^4 + ]+ , -- herleid de afgeleide tot 1 breuk+ [ (2*x^4+3)/x^2, (2*x^5-5)/x^3, (4*x^5-1)/x^2, (4*x^4+3)/x^3+ , (3*x-1)/(7*x^2), (2*x^3+1)/(3*x^4), (x^2-2)/(3*x^3), (x+5)/(6*x^3)+ ]+ ]+ +-- differentieer x^r (r uit R), noteer zonder negatieve en gebroken exponent+diffSet6 :: [[Expr]]+diffSet6 = differentiateLists $+ let x = Var "x" in+ [ [ x*root x 3, x^3*sqrt x, x*root x 5, x^4*sqrt x, 1/(x*root x 3)+ , 1/(x^3*sqrt x), 1/(x*root x 5), 1/(x^4*sqrt x)+ ]+ , [ x^2*root (x^2) 3, x*root (x^3) 4, x^3*root (x^2) 5, x^2*root (x^3) 5+ , (x^3+1)*(2+sqrt x), (3+x^2)*(1+root x 3), (x^2+1)*(root x 3+2)+ , (3+x^3)*(sqrt x+1) + ]+ , [ (sqrt x + 1)^2, (x*sqrt x-3)^2, (sqrt x-2)^2, (x*sqrt x+1)^2+ , (x+2)/sqrt x, (x-3)/sqrt x, (x-4)/sqrt x, (x+5)/sqrt x+ ]+ , [ (x-2)/(x*sqrt x), (x+3)/(x*sqrt x), (x+4)/(x*sqrt x), (x-5)/(x*sqrt x)+ , (x^2+2)/(3*sqrt x), (x^2-3)/(4*sqrt x)+ , (x^2+4)/(2*sqrt x), (x^2-6)/(3*sqrt x)+ ]+ , [ (x+3)/(x^2*sqrt x), (x-1)/(x^3*sqrt x), (x-2)/(x^2*sqrt x)+ , (x+4)/(x^3*sqrt x), (sqrt x-2)/x^2, (2*sqrt x+1)/x^2+ , (1-sqrt x)/x, (3*sqrt x+2)/x+ ]+ ]+ +-- differentieren met de kettingregel+diffSet7 :: [[Expr]]+diffSet7 = differentiateLists $+ let x = Var "x" in+ [ [ 2*(x^2+3*x)^5, 3*(x^3-4*x)^6, -6*(x^2+2*x)^4, -5*(x^3-3*x^2)^3]+ , [ -(2/(x^2+3*x)^5),-(3/(x^3-4*x)^6), 6/(x^2+2*x)^4, 5/(x^3-3*x^2)^3]+ , [ sqrt (3*x^4-x), sqrt (x^3+5*x^2), sqrt (6*x^2+x), sqrt (7*x^3-3*x^2)]+ , [ 1/sqrt (3*x-2), 1/sqrt (8*x+5), 1/sqrt (3*x-4), 1/sqrt (5*x-2)]+ , [ (2*x-1)^2*sqrt (2*x-1), (3*x^2+2)*sqrt (3*x^2+2)+ , (3*x+5)^3*sqrt (3*x+5), (4*x^3-7)*sqrt (4*x^3-7)+ ]+ ]+ +-- differentieren met de kettingregel gecombineerd+diffSet8 :: [[Expr]]+diffSet8 = differentiateLists $+ let x = Var "x" in+ [ [ 2*x*sqrt (4*x+3), 3*x*sqrt (2*x-5), 4*x*sqrt (3*x+2), 2*x*sqrt (5*x-3)]+ , [ x^2*(4*x^2-2)^3, x^3*(3*x-4)^3, x^4*(3*x^2+1)^5, x^5*(4*x+3)^4]+ , [ (x+3)/sqrt (2*x-1), (x+7)/sqrt (4*x+3)+ , (x-2)/sqrt (3*x+1), (x-7)/sqrt (5*x-4) + ]+ , [ sqrt (2*x^2-1)/(x+3), sqrt (4*x^2+3)/(x+7)+ , sqrt (3*x^2+1)/(x-2), sqrt (5*x^2-4)/(x-7)+ ]+ ]
src/Domain/Math/Expr.hs view
@@ -10,15 +10,15 @@ -- ----------------------------------------------------------------------------- module Domain.Math.Expr - ( module Domain.Math.Expr.Data+ ( module Common.Rewriting.Term+ , module Domain.Math.Expr.Data , module Domain.Math.Expr.Parser- , module Domain.Math.Expr.Symbolic , module Domain.Math.Expr.Symbols , module Domain.Math.Expr.Views ) where import Domain.Math.Expr.Data import Domain.Math.Expr.Parser-import Domain.Math.Expr.Symbolic import Domain.Math.Expr.Symbols import Domain.Math.Expr.Views+import Common.Rewriting.Term hiding (Term(..))
src/Domain/Math/Expr/Data.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XDeriveDataTypeable #-}+{-# LANGUAGE DeriveDataTypeable #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -12,18 +12,18 @@ ----------------------------------------------------------------------------- module Domain.Math.Expr.Data where -import Data.Char (isAlphaNum)-import Data.Ratio-import Data.Typeable-import Test.QuickCheck-import Control.Monad+import Common.Rewriting import Common.Uniplate import Common.Utils (commaList) import Common.View-import Common.Rewriting hiding (operators)-import Domain.Math.Expr.Symbolic+import Control.Monad+import Data.Char (isAlphaNum)+import Data.Maybe+import Data.Ratio+import Data.Typeable+import Domain.Math.Data.Relation (relationSymbols) import Domain.Math.Expr.Symbols-+import Test.QuickCheck import qualified Common.Rewriting.Term as Term -----------------------------------------------------------------------@@ -88,23 +88,17 @@ asinh = unary asinhSymbol atanh = unary atanhSymbol acosh = unary acoshSymbol - -instance Symbolic Expr where- variable = Var- - getVariable (Var s) = return s- getVariable _ = mzero- ++instance WithFunctions Expr where function s [a, b] | s == plusSymbol = a :+: b | s == timesSymbol = a :*: b | s == minusSymbol = a :-: b | s == divideSymbol = a :/: b- | s == rootSymbol && b == Nat 2 = Sqrt a+ | isRootSymbol s && b == Nat 2 = Sqrt a function s [a] | s == negateSymbol = Negate a- function s as = - Sym s as+ function s as = Sym s as getFunction expr = case expr of@@ -115,8 +109,13 @@ a :/: b -> return (divideSymbol, [a, b]) Sqrt a -> return (rootSymbol, [a, Nat 2]) Sym s as -> return (s, as)- _ -> mzero+ _ -> fail "Expr.getFunction" +instance WithVars Expr where+ variable = Var+ getVariable (Var s) = return s+ getVariable _ = fail "Expr.getVariable"+ fromDouble :: Double -> Expr fromDouble d | d < 0 = negate (Number (abs d))@@ -144,16 +143,16 @@ instance CoArbitrary Expr where coarbitrary expr = case expr of - a :+: b -> variant 0 . coarbitrary a . coarbitrary b- a :*: b -> variant 1 . coarbitrary a . coarbitrary b- a :-: b -> variant 2 . coarbitrary a . coarbitrary b- Negate a -> variant 3 . coarbitrary a- Nat n -> variant 4 . coarbitrary n- a :/: b -> variant 5 . coarbitrary a . coarbitrary b- Number d -> variant 6 . coarbitrary d- Sqrt a -> variant 7 . coarbitrary a- Var s -> variant 8 . coarbitrary s- Sym f xs -> variant 9 . coarbitrary (show f) . coarbitrary xs+ a :+: b -> variant (0 :: Int) . coarbitrary a . coarbitrary b+ a :*: b -> variant (1 :: Int) . coarbitrary a . coarbitrary b+ a :-: b -> variant (2 :: Int) . coarbitrary a . coarbitrary b+ Negate a -> variant (3 :: Int) . coarbitrary a+ Nat n -> variant (4 :: Int) . coarbitrary n+ a :/: b -> variant (5 :: Int) . coarbitrary a . coarbitrary b+ Number d -> variant (6 :: Int) . coarbitrary d+ Sqrt a -> variant (7 :: Int) . coarbitrary a+ Var s -> variant (8 :: Int) . coarbitrary s+ Sym f xs -> variant (9 :: Int) . coarbitrary (show f) . coarbitrary xs symbolGenerator :: (Int -> [Gen Expr]) -> [(Symbol, Maybe Int)] -> Int -> Gen Expr symbolGenerator extras syms = f @@ -171,9 +170,9 @@ natGenerator = liftM (Nat . abs) arbitrary varGenerator :: [String] -> Gen Expr-varGenerator vars- | null vars = error "varGenerator: empty list"- | otherwise = oneof [ return (Var x) | x <- vars ]+varGenerator xs+ | null xs = error "varGenerator: empty list"+ | otherwise = oneof [ return (Var x) | x <- xs ] ----------------------------------------------------------------------- -- Pretty printer @@ -184,6 +183,7 @@ showExpr :: OperatorTable -> Expr -> String showExpr table = rec 0 where+ rec :: Int -> Expr -> String rec _ (Nat n) = if n>=0 then show n else "(ERROR)" ++ show n rec _ (Number d) = if d>=0 then show d else "(ERROR)" ++ show d rec _ (Var s) @@ -191,28 +191,30 @@ | otherwise = "\"" ++ s ++ "\"" rec i expr = case getFunction expr of+ Just (s1, [Sym s2 [Var x, a]]) | s1 == diffSymbol && s2 == lambdaSymbol ->+ parIf (i>10000) $ "D(" ++ x ++ ") " ++ rec 10001 a -- To do: remove special case for sqrt- Just (s, [a, b]) | s == rootSymbol && b == Nat 2 -> + Just (s, [a, b]) | isRootSymbol s && b == Nat 2 -> parIf (i>10000) $ unwords ["sqrt", rec 10001 a] Just (s, xs) | s == listSymbol -> "[" ++ commaList (map (rec 0) xs) ++ "]" Just (s, as) -> case (lookup s symbolTable, as) of (Just (InfixLeft, n, op), [x, y]) -> - parIf (i>n) $ concat [rec n x, op, rec (n+1) y]+ parIf (i>n) $ rec n x ++ op ++ rec (n+1) y (Just (InfixRight, n, op), [x, y]) -> - parIf (i>n) $ concat [rec (n+1) x, op, rec n y]+ parIf (i>n) $ rec (n+1) x ++ op ++ rec n y (Just (InfixNon, n, op), [x, y]) -> - parIf (i>n) $ concat [rec (n+1) x, op, rec (n+1) y]+ parIf (i>n) $ rec (n+1) x ++ op ++ rec (n+1) y (Just (PrefixNon, n, op), [x]) ->- parIf (i>=n) $ concat [op, rec (n+1) x]+ parIf (i>=n) $ op ++ rec (n+1) x _ -> parIf (not (null as) && i>10000) $ unwords (showSymbol s : map (rec 10001) as) Nothing -> error "showExpr" showSymbol s- | s == rootSymbol = "root"+ | isRootSymbol s = "root" | otherwise = show s symbolTable = [ (s, (a, n, op)) | (n, (a, xs)) <- zip [1..] table, (s, op) <- xs ]@@ -220,16 +222,22 @@ parIf b = if b then par else id par s = "(" ++ s ++ ")" -instance ShallowEq Expr where- shallowEq (Nat a) (Nat b) = a == b- shallowEq (Var a) (Var b) = a == b- shallowEq (Number a) (Number b) = a == b- shallowEq expr1 expr2 =- case (getFunction expr1, getFunction expr2) of- (Just (s1, as), Just (s2, bs)) -> - s1 == s2 && length as == length bs- _ -> False +type OperatorTable = [(Associativity, [(Symbol, String)])] +data Associativity = InfixLeft | InfixRight | PrefixNon+ | InfixNon+ deriving (Show, Eq)++operatorTable :: OperatorTable+operatorTable =+ (InfixNon, [ (s, op) | (_, (op, s)) <- relationSymbols]) :+ [ (InfixLeft, [(plusSymbol, "+"), (minusSymbol, "-")]) -- 6+ , (PrefixNon, [(negateSymbol, "-")]) -- 6++ , (InfixLeft, [(timesSymbol, "*"), (divideSymbol, "/")]) -- 7+ , (InfixRight, [(powerSymbol, "^")]) -- 8+ ]++ instance Rewrite Expr instance Different Expr where@@ -241,57 +249,30 @@ toTerm (Var v) = Term.Var v toTerm expr = case getFunction expr of- Just (s, xs) -> Term.makeConTerm s (map toTerm xs)+ Just (s, xs) -> function s (map toTerm xs) Nothing -> error "IsTerm Expr" fromTerm (Term.Num n) = return (fromInteger n) fromTerm (Term.Float d) = return (Number d) fromTerm (Term.Var v) = return (Var v) fromTerm t =- case Term.getSpine t of- (Term.Con s, xs) -> do+ case getFunction t of+ Just (s, xs) -> do ys <- mapM fromTerm xs return (function s ys) _ -> fail "fromTerm" instance IsTerm a => IsTerm [a] where toTerm = function listSymbol . map toTerm- fromTerm a = isSymbol listSymbol a >>= mapM fromTerm+ fromTerm a = do+ xs <- isFunction listSymbol a+ mapM fromTerm xs toExpr :: IsTerm a => a -> Expr-toExpr a =- case fromTerm (toTerm a) of- Just expr -> expr- Nothing -> error "Invalid term"+toExpr = fromJust . fromTerm . toTerm fromExpr :: (MonadPlus m, IsTerm a) => Expr -> m a fromExpr = fromTerm . toTerm exprView :: IsTerm a => View Expr a exprView = makeView fromExpr toExpr---------------------------------------------------------------------------- AC Theory for expression-{--exprACs :: Operators Expr-exprACs = [plusOperator, timesOperator]--plusOperator, timesOperator :: Operator Expr-plusOperator = acOperator (+) isPlus-timesOperator = acOperator (*) isTimes--collectPlus, collectTimes :: Expr -> [Expr]-collectPlus = collectWithOperator plusOperator-collectTimes = collectWithOperator timesOperator--size :: Expr -> Int-size e = 1 + compos 0 (+) size e--}-collectVars :: Expr -> [String]-collectVars e = [ s | Var s <- universe e ]--hasVars :: Expr -> Bool-hasVars = not . noVars--noVars :: Expr -> Bool-noVars = null . collectVars
src/Domain/Math/Expr/Parser.hs view
@@ -17,20 +17,20 @@ import Prelude hiding ((^)) import Text.Parsing-import Control.Monad+import Control.Monad.Error+import Common.Rewriting import Common.Transformation import qualified Domain.Logic.Formula as Logic import Domain.Logic.Formula (Logic) import Domain.Math.Data.Relation import Domain.Math.Expr.Data-import Domain.Math.Expr.Symbolic import Domain.Math.Expr.Symbols import Domain.Math.Data.OrList import Test.QuickCheck (arbitrary) scannerExpr :: Scanner scannerExpr = defaultScanner - { keywords = ["sqrt", "root", "and", "or", "true", "false"]+ { keywords = ["sqrt", "root", "log", "and", "or", "true", "false", "D"] , keywordOperators = ["==", "<=", ">=", "<", ">", "~=", "+", "-", "*", "^", "/"] , operatorCharacters = "+-*/^.=<>~" , qualifiedIdentifiers = True@@ -67,9 +67,15 @@ -- To fix: sqrt expects exactly one argument <|> (\xs -> function rootSymbol (xs ++ [2])) <$ pKey "sqrt" <|> function rootSymbol <$ pKey "root"+ <|> function logSymbol <$ pKey "log"+ <|> makeDiff <$ pKey "D"+ where+ makeDiff [x,a] = unary diffSymbol (binary lambdaSymbol x a)+ makeDiff _ = symbol bottomSymbol qualifiedSymb :: TokenParser ([Expr] -> Expr)-qualifiedSymb = (function . uncurry makeSymbol) <$> (pQVarid <|> pQConid)+qualifiedSymb = f <$> (pQVarid <|> pQConid)+ where f (a, b) = function $ newSymbol (a, b) pEquations :: TokenParser a -> TokenParser (Equations a) pEquations = pLines True . pEquation@@ -101,7 +107,6 @@ pTerm = return <$> p <|> true <$ pKey "true" <|> false <$ pKey "false"- pSepList p q = (:) <$> p <*> pList (q *> p) pLogic :: TokenParser a -> TokenParser (Logic a) pLogic p = levelOr
− src/Domain/Math/Expr/Symbolic.hs
@@ -1,121 +0,0 @@-{-# LANGUAGE TypeSynonymInstances #-}--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.Math.Expr.Symbolic - ( module Domain.Math.Expr.Symbolic, Symbol- ) where--import Control.Monad-import Data.Maybe-import Common.Rewriting.Term-import qualified Text.OpenMath.Symbol as OM--makeSymbol :: String -> String -> Symbol-makeSymbol = S . Just--class IsSymbol a where- toSymbol :: a -> Symbol- fromSymbol :: Symbol -> a--instance IsSymbol Symbol where- toSymbol = id- fromSymbol = id--instance IsSymbol String where- toSymbol = S Nothing- fromSymbol (S (Just a) b) = a ++ "." ++ b- fromSymbol (S Nothing b) = b--instance IsSymbol OM.Symbol where- toSymbol s = S (OM.dictionary s) (OM.symbolName s) - fromSymbol (S (Just a) b) = OM.makeSymbol a b- fromSymbol (S Nothing b) = OM.extraSymbol b--stringToSymbol :: String -> Symbol-stringToSymbol s = - case break (=='.') s of- (xs, _:ys) -> S (Just xs) ys- _ -> S Nothing s------------------------------------------------------------------------ Type class for symbolic representations--class Symbolic a where- -- constructing- variable :: String -> a- symbol :: Symbol -> a- function :: Symbol -> [a] -> a- -- matching- getVariable :: MonadPlus m => a -> m String- getSymbol :: MonadPlus m => a -> m Symbol- getFunction :: MonadPlus m => a -> m (Symbol, [a])- isSymbol :: MonadPlus m => Symbol -> a -> m [a]- -- default definition- symbol s = function s []- getSymbol a = do- (t, as) <- getFunction a - guard (null as)- return t- isSymbol s a = do- (t, as) <- getFunction a- guard (s==t)- return as- -instance Symbolic Term where - variable = Var- symbol = Con- function = makeConTerm- getVariable = isVar- getSymbol = isCon- getFunction = getConSpine- -nullary :: (IsSymbol s, Symbolic a) => s -> a-nullary = symbol . toSymbol- -unary :: (IsSymbol s, Symbolic a) => s -> a -> a-unary f a = function (toSymbol f) [a]--binary :: (IsSymbol s, Symbolic a) => s -> a -> a -> a-binary f a b = function (toSymbol f) [a, b]--isConst :: (IsSymbol s, Symbolic a) => s -> a -> Bool-isConst s = maybe False null . isSymbol (toSymbol s) --isVariable :: Symbolic a => a -> Bool-isVariable = isJust . getVariable--isUnary :: (IsSymbol s, Symbolic a, MonadPlus m) => s -> a -> m a-isUnary s a = - case isSymbol (toSymbol s) a of- Just [x] -> return x- _ -> mzero--isBinary :: (IsSymbol s, Symbolic a, MonadPlus m) => s -> a -> m (a, a)-isBinary s a = - case isSymbol (toSymbol s) a of- Just [x, y] -> return (x, y)- _ -> mzero---- left-associative by default-isAssoBinary :: (IsSymbol s, Symbolic a, MonadPlus m) => s -> a -> m (a, a)-isAssoBinary s a =- case isSymbol (toSymbol s) a of- Just [x, y] -> return (x, y)- Just (x:xs) | length xs > 1 -> return (x, function (toSymbol s) xs)- _ -> mzero- -fromTermWith :: (MonadPlus m, IsSymbol s, IsTerm a) - => (s -> [a] -> m a) -> Term -> m a-fromTermWith f term = do- (s, xs) <- getFunction term- ys <- mapM fromTermM xs- f (fromSymbol s) ys
src/Domain/Math/Expr/Symbols.hs view
@@ -8,103 +8,131 @@ -- Stability : provisional -- Portability : portable (depends on ghc) ----- Exports relevant OpenMath symbols, converted to the --- Symbol data type from @Common.Rewriting@.+-- Exports relevant OpenMath symbols -- ------------------------------------------------------------------------------module Domain.Math.Expr.Symbols where+module Domain.Math.Expr.Symbols+ ( openMathSymbol+ -- OpenMath dictionary symbols+ , plusSymbol, timesSymbol, minusSymbol, divideSymbol, rootSymbol+ , powerSymbol, negateSymbol, sinSymbol, cosSymbol, lnSymbol+ , diffSymbol, piSymbol, lambdaSymbol, listSymbol+ , absSymbol, signumSymbol, logSymbol, expSymbol, tanSymbol, asinSymbol+ , atanSymbol, acosSymbol, sinhSymbol, tanhSymbol, coshSymbol, asinhSymbol+ , atanhSymbol, acoshSymbol, bottomSymbol, fcompSymbol+ -- Matching+ , isPlus, isTimes, isMinus, isDivide, isPower, isNegate, isRoot+ , isPowerSymbol, isRootSymbol, isLogSymbol, isDivideSymbol+ , (^), root+ ) where +import Common.Id+import Common.Rewriting import Control.Monad-import Domain.Math.Expr.Symbolic-import Domain.Math.Data.Relation (relationSymbols)+import Prelude hiding ((^))+import qualified Text.OpenMath.Dictionary.Arith1 as OM+import qualified Text.OpenMath.Dictionary.Calculus1 as OM+import qualified Text.OpenMath.Dictionary.Fns1 as OM+import qualified Text.OpenMath.Dictionary.List1 as OM+import qualified Text.OpenMath.Dictionary.Nums1 as OM+import qualified Text.OpenMath.Dictionary.Transc1 as OM+import qualified Text.OpenMath.Symbol as OM --- OpenMath dictionaries-import qualified Text.OpenMath.Dictionary.Arith1 as Arith1-import qualified Text.OpenMath.Dictionary.Calculus1 as Calculus1-import qualified Text.OpenMath.Dictionary.Fns1 as Fns1-import qualified Text.OpenMath.Dictionary.List1 as List1-import qualified Text.OpenMath.Dictionary.Nums1 as Nums1-import qualified Text.OpenMath.Dictionary.Transc1 as Transc1+-- | Conversion function+openMathSymbol :: OM.Symbol -> Symbol+openMathSymbol s = newSymbol (OM.dictionary s # OM.symbolName s) ---------------------------------------------------------------- Converted OpenMath symbols+-- Arith1 dictionary -plusSymbol, timesSymbol, minusSymbol, divideSymbol,- rootSymbol, powerSymbol, negateSymbol :: Symbol-plusSymbol = toSymbol Arith1.plusSymbol-timesSymbol = toSymbol Arith1.timesSymbol-minusSymbol = toSymbol Arith1.minusSymbol -divideSymbol = toSymbol Arith1.divideSymbol-rootSymbol = toSymbol Arith1.rootSymbol-powerSymbol = toSymbol Arith1.powerSymbol-negateSymbol = toSymbol Arith1.unaryMinusSymbol+plusSymbol, timesSymbol, minusSymbol, divideSymbol, rootSymbol,+ powerSymbol, negateSymbol, absSymbol :: Symbol+ +plusSymbol = openMathSymbol OM.plusSymbol+timesSymbol = openMathSymbol OM.timesSymbol+minusSymbol = openMathSymbol OM.minusSymbol+divideSymbol = openMathSymbol OM.divideSymbol+rootSymbol = openMathSymbol OM.rootSymbol+powerSymbol = openMathSymbol OM.powerSymbol+negateSymbol = openMathSymbol OM.unaryMinusSymbol+absSymbol = openMathSymbol OM.absSymbol -sinSymbol, cosSymbol, lnSymbol :: Symbol-sinSymbol = toSymbol Transc1.sinSymbol-cosSymbol = toSymbol Transc1.cosSymbol-lnSymbol = toSymbol Transc1.lnSymbol+-------------------------------------------------------------+-- Transc1 dictionary -diffSymbol, piSymbol, lambdaSymbol, listSymbol :: Symbol-diffSymbol = toSymbol Calculus1.diffSymbol-piSymbol = toSymbol Nums1.piSymbol-lambdaSymbol = toSymbol Fns1.lambdaSymbol-listSymbol = toSymbol List1.listSymbol+logSymbol, sinSymbol, cosSymbol, lnSymbol, expSymbol, tanSymbol,+ sinhSymbol, tanhSymbol, coshSymbol :: Symbol ----------------------------------------------------------------- Operator fixities+logSymbol = openMathSymbol OM.logSymbol+sinSymbol = openMathSymbol OM.sinSymbol+cosSymbol = openMathSymbol OM.cosSymbol+lnSymbol = openMathSymbol OM.lnSymbol+expSymbol = openMathSymbol OM.expSymbol +tanSymbol = openMathSymbol OM.tanSymbol+sinhSymbol = openMathSymbol OM.sinhSymbol+tanhSymbol = openMathSymbol OM.tanhSymbol+coshSymbol = openMathSymbol OM.coshSymbol -type OperatorTable = [(Associativity, [(Symbol, String)])]+-------------------------------------------------------------+-- Other dictionaries -data Associativity = InfixLeft | InfixRight | PrefixNon- | InfixNon- deriving (Show, Eq)+diffSymbol, lambdaSymbol, listSymbol, piSymbol :: Symbol -operatorTable :: OperatorTable-operatorTable =- (InfixNon, [ (s, op) | (_, (op, s)) <- relationSymbols]) :- [ (InfixLeft, [(plusSymbol, "+"), (minusSymbol, "-")]) -- 6- , (PrefixNon, [(negateSymbol, "-")]) -- 6+- , (InfixLeft, [(timesSymbol, "*"), (divideSymbol, "/")]) -- 7- , (InfixRight, [(powerSymbol, "^")]) -- 8- ]+diffSymbol = openMathSymbol OM.diffSymbol+lambdaSymbol = openMathSymbol OM.lambdaSymbol+listSymbol = openMathSymbol OM.listSymbol+piSymbol = openMathSymbol OM.piSymbol ------------------------------------------------------------- -- Extra math symbols -absSymbol = toSymbol "abs" -signumSymbol = toSymbol "signum" -logSymbol = toSymbol "log" -- in Haskell, logbase e = log-expSymbol = toSymbol "exp" -- exp 1 ~= 2.718-tanSymbol = toSymbol "tan" -asinSymbol = toSymbol "asin" -atanSymbol = toSymbol "atan" -acosSymbol = toSymbol "acos" -sinhSymbol = toSymbol "sinh" -tanhSymbol = toSymbol "tanh" -coshSymbol = toSymbol "cosh" -asinhSymbol = toSymbol "asinh" -atanhSymbol = toSymbol "atanh" -acoshSymbol = toSymbol "acosh" -bottomSymbol = toSymbol "error"-fcompSymbol = toSymbol "compose"+signumSymbol, asinSymbol, atanSymbol, acosSymbol, asinhSymbol, atanhSymbol,+ acoshSymbol, bottomSymbol, fcompSymbol :: Symbol +signumSymbol = newSymbol "signum" +asinSymbol = newSymbol "asin" +atanSymbol = newSymbol "atan" +acosSymbol = newSymbol "acos" +asinhSymbol = newSymbol "asinh" +atanhSymbol = newSymbol "atanh" +acoshSymbol = newSymbol "acosh" +bottomSymbol = newSymbol "error"+fcompSymbol = newSymbol "compose"+ ------------------------------------------------------------- -- Some match functions -isPlus, isTimes, isMinus, isDivide :: - (Symbolic a, MonadPlus m) => a -> m (a, a)-isNegate :: (Symbolic a, MonadPlus m) => a -> m a+isPlus, isTimes, isMinus, isDivide, isPower, isRoot :: + (WithFunctions a, MonadPlus m) => a -> m (a, a)+isNegate :: (WithFunctions a, MonadPlus m) => a -> m a isPlus = isAssoBinary plusSymbol isTimes = isAssoBinary timesSymbol isMinus = isBinary minusSymbol isDivide = isBinary divideSymbol isNegate = isUnary negateSymbol +isPower = isBinary powerSymbol+isRoot = isBinary rootSymbol +isPowerSymbol, isRootSymbol, isLogSymbol, isDivideSymbol :: Symbol -> Bool++isPowerSymbol = (== powerSymbol)+isRootSymbol = (== rootSymbol)+isLogSymbol = (== logSymbol)+isDivideSymbol = (== divideSymbol)+ infixr 8 ^ -(^) :: Symbolic a => a -> a -> a+(^) :: WithFunctions a => a -> a -> a (^) = binary powerSymbol -root :: Symbolic a => a -> a -> a+root :: WithFunctions a => a -> a -> a root = binary rootSymbol++-- left-associative+isAssoBinary :: (WithFunctions a, Monad m) => Symbol -> a -> m (a, a)+isAssoBinary s a =+ case isFunction s a of+ Just [x, y] -> return (x, y)+ Just (x:xs) | length xs > 1 -> return (x, function s xs)+ _ -> fail "isAssoBinary"
src/Domain/Math/Expr/Views.hs view
@@ -12,25 +12,34 @@ module Domain.Math.Expr.Views where import Prelude hiding (recip, (^))+import Common.Rewriting import Common.View import Domain.Math.Expr.Data import Domain.Math.Expr.Symbols-import Data.List (nub)+import qualified Data.Set as S ------------------------------------------------------------ -- Smart constructors +infixr 8 .^.+infixl 7 .*., ./.+infixl 6 .-., .+.+ (.+.) :: Expr -> Expr -> Expr-Nat 0 .+. b = b-a .+. Nat 0 = a-a .+. Negate b = a .-. b-a .+. b = a :+: b+Nat 0 .+. b = b+a .+. Nat 0 = a+a .+. Negate b = a .-. b+a .+. (b :+: c) = (a .+. b) .+. c+a .+. (b :-: c) = (a .+. b) .-. c+a .+. b = a :+: b (.-.) :: Expr -> Expr -> Expr-Nat 0 .-. b = neg b-a .-. Nat 0 = a-a .-. Negate b = a .+. b-a .-. b = a :-: b+Nat 0 .-. b = neg b+a .-. Nat 0 = a+a .-. Negate b = a .+. b+a .-. (b :+: c) = (a .-. b) .-. c+a .-. (b :-: c) = (a .-. b) .+. c+a .-. b = a :-: b neg :: Expr -> Expr neg (Nat 0) = 0@@ -40,14 +49,15 @@ neg a = Negate a (.*.) :: Expr -> Expr -> Expr-Nat 0 .*. _ = Nat 0-_ .*. Nat 0 = Nat 0-Nat 1 .*. b = b-a .*. Nat 1 = a-Negate a .*. b = neg (a .*. b)-a .*. Negate b = neg (a .*. b)+Nat 0 .*. _ = Nat 0+_ .*. Nat 0 = Nat 0+Nat 1 .*. b = b+a .*. Nat 1 = a+Negate a .*. b = neg (a .*. b)+a .*. Negate b = neg (a .*. b) a .*. (Nat 1 :/: b) = a ./. b-a .*. b = a :*: b+a .*. (b :*: c) = (a .*. b) .*. c+a .*. b = a :*: b (./.) :: Expr -> Expr -> Expr a ./. Nat 1 = a@@ -129,32 +139,32 @@ simpleProductView :: View Expr (Bool, [Expr]) simpleProductView = makeView (Just . second ($ []) . f) g where- f (a :*: b) = f a &&& f b+ f (a :*: b) = f a .&&. f b f (Negate a) = first not (f a) f e = (False, (e:)) - (n1, g1) &&& (n2, g2) = (n1 /= n2, g1 . g2)+ (n1, g1) .&&. (n2, g2) = (n1 /= n2, g1 . g2) g (b, xs) = (if b then neg else id) (foldl (.*.) 1 xs) productView :: View Expr (Bool, [Expr]) productView = makeView (Just . second ($ []) . f False) g where- f r (a :*: b) = f r a &&& f r b+ f r (a :*: b) = f r a .&&. f r b f r (a :/: b) = case a of -- two special cases (for efficiency) Nat 1 -> f (not r) b Negate (Nat 1) -> first not (f (not r) b)- _ -> f r a &&& f (not r) b+ _ -> f r a .&&. f (not r) b f r (Negate a) = first not (f r a) f r e = (False, if r then (recip e:) else (e:)) - (n1, g1) &&& (n2, g2) = (n1 /= n2, g1 . g2)+ (n1, g1) .&&. (n2, g2) = (n1 /= n2, g1 . g2) g (b, xs) = (if b then neg else id) (foldl (.*.) 1 xs) -- helper to determine the name of the variable (move to a different module?) selectVar :: Expr -> Maybe String-selectVar = f . nub . collectVars+selectVar = f . S.toList . varSet where f [] = Just "x" -- exceptional case (e.g., for constants) f [a] = Just a
src/Domain/Math/Numeric/Exercises.hs view
@@ -27,42 +27,43 @@ ------------------------------------------------------------ -- Exercises -numericExercise :: LabeledStrategy Expr -> Exercise Expr+numericExercise :: LabeledStrategy (Context Expr) -> Exercise Expr numericExercise s = makeExercise { status = Alpha , parser = parseExpr , equivalence = viewEquivalent rationalView- , strategy = mapRules liftToContext s+ , strategy = s+ , navigation = termNavigator } naturalExercise :: Exercise Expr naturalExercise = (numericExercise naturalStrategy)- { description = "simplify expression (natural numbers)"- , exerciseCode = makeCode "math" "natural"+ { exerciseId = describe "simplify expression (natural numbers)" $ + newId "numbers.natural" , isReady = (`belongsTo` integerNormalForm) , examples = concat calculateResults } integerExercise :: Exercise Expr integerExercise = (numericExercise integerStrategy)- { description = "simplify expression (integers)"- , exerciseCode = makeCode "math" "integer"+ { exerciseId = describe "simplify expression (integers)" $ + newId "numbers.integers" , isReady = (`belongsTo` integerNormalForm) , examples = concat calculateResults } rationalExercise :: Exercise Expr rationalExercise = (numericExercise rationalStrategy)- { description = "simplify expression (rational numbers)"- , exerciseCode = makeCode "math" "rational"+ { exerciseId = describe "simplify expression (rational numbers)" $ + newId "numbers.rational" , isReady = (`belongsTo` rationalNormalForm) , randomExercise = simpleGenerator (rationalGenerator 5) } fractionExercise :: Exercise Expr fractionExercise = (numericExercise fractionStrategy)- { description = "simplify expression (fractions)"- , exerciseCode = makeCode "math" "fraction"+ { exerciseId = describe "simplify expression (fractions)" $ + newId "arithmetic.fractions" , isReady = (`belongsTo` rationalNormalForm) , randomExercise = simpleGenerator (rationalGenerator 5) }
src/Domain/Math/Numeric/Laws.hs view
@@ -16,91 +16,88 @@ , fracLaws, testFracLaws, testFracLawsWith ) where +import Common.TestSuite import Test.QuickCheck -testNumLaws :: Num a => String -> Gen a -> IO ()+testNumLaws :: Num a => String -> Gen a -> TestSuite testNumLaws = testNumLawsWith (==) -testNumLawsWith :: Num a => (a -> a -> Bool) -> String -> Gen a -> IO ()-testNumLawsWith eq s g = do- putStrLn $ "Testing Num instance for " ++ s+testNumLawsWith :: Num a => (a -> a -> Bool) -> String -> Gen a -> TestSuite+testNumLawsWith eq s g = suite ("Num instance for " ++ s) $ mapM_ ($ g) (numLaws eq) -testFracLaws :: Fractional a => String -> Gen a -> IO ()+testFracLaws :: Fractional a => String -> Gen a -> TestSuite testFracLaws = testFracLawsWith (==) -testFracLawsWith :: Fractional a => (a -> a -> Bool) -> String -> Gen a -> IO ()-testFracLawsWith eq s g = do- putStrLn $ "Testing Fractional instance for " ++ s+testFracLawsWith :: Fractional a => (a -> a -> Bool) -> String -> Gen a -> TestSuite+testFracLawsWith eq s g = suite ("Fractional instance for " ++ s) $ mapM_ ($ g) (fracLaws eq) -numLaws :: Num a => (a -> a -> Bool) -> [Gen a -> IO ()]+numLaws :: Num a => (a -> a -> Bool) -> [Gen a -> TestSuite] numLaws eq =- [ law1 "plus zero left" $ \a -> 0+a == a- , law1 "plus zero right" $ \a -> a+0 == a- , law2 "plus comm" $ \a b -> a+b == b+a- , law3 "plus trans" $ \a b c -> a+(b+c) == (a+b)+c- , law1 "negate zero" $ \a -> -0 == 0 `asTypeOf` a- , law1 "negate double" $ \a -> -(-a) == a- , law1 "minus zero left" $ \a -> 0-a == -a- , law1 "minus zero right" $ \a -> a-0 == a- , law2 "negate plus" $ \a b -> -(a+b) == -a-b- , law2 "negate minus" $ \a b -> -(a-b) == -a+b- , law2 "plus negate" $ \a b -> a+(-b) == a-b- , law1 "times zero left" $ \a -> 0*a == 0- , law1 "times zero right" $ \a -> a*0 == 0- , law1 "times one left" $ \a -> 1*a == a- , law1 "times one right" $ \a -> a*1 == a- , law2 "times comm" $ \a b -> a*b == b*a- , law3 "times trans" $ \a b c -> a*(b*c) == (a*b)*c- , law2 "times negate left" $ \a b -> (-a)*b == -(a*b)- , law2 "times negate right" $ \a b -> a*(-b) == -(a*b)- , law3 "times plus left" $ \a b c -> (a+b)*c == a*c + b*c- , law3 "times plus right" $ \a b c -> a*(b+c) == a*b + a*c- , law3 "times minus left" $ \a b c -> (a-b)*c == a*c - b*c- , law3 "times minus right" $ \a b c -> a*(b-c) == a*b - a*c+ [ law1 "plus zero left" $ \a -> 0+a === a+ , law1 "plus zero right" $ \a -> a+0 === a+ , law2 "plus comm" $ \a b -> a+b === b+a+ , law3 "plus trans" $ \a b c -> a+(b+c) === (a+b)+c+ , law1 "negate zero" $ \a -> -0 === 0 `asTypeOf` a+ , law1 "negate double" $ \a -> -(-a) === a+ , law1 "minus zero left" $ \a -> 0-a === -a+ , law1 "minus zero right" $ \a -> a-0 === a+ , law2 "negate plus" $ \a b -> -(a+b) === -a-b+ , law2 "negate minus" $ \a b -> -(a-b) === -a+b+ , law2 "plus negate" $ \a b -> a+(-b) === a-b+ , law1 "times zero left" $ \a -> 0*a === 0+ , law1 "times zero right" $ \a -> a*0 === 0+ , law1 "times one left" $ \a -> 1*a === a+ , law1 "times one right" $ \a -> a*1 === a+ , law2 "times comm" $ \a b -> a*b === b*a+ , law3 "times trans" $ \a b c -> a*(b*c) === (a*b)*c+ , law2 "times negate left" $ \a b -> (-a)*b === -(a*b)+ , law2 "times negate right" $ \a b -> a*(-b) === -(a*b)+ , law3 "times plus left" $ \a b c -> (a+b)*c === a*c + b*c+ , law3 "times plus right" $ \a b c -> a*(b+c) === a*b + a*c+ , law3 "times minus left" $ \a b c -> (a-b)*c === a*c - b*c+ , law3 "times minus right" $ \a b c -> a*(b-c) === a*b - a*c ] where- infix 4 ==- a == b = property (a `eq` b)+ infix 4 ===+ a === b = property (a `eq` b) -fracLaws :: Fractional a => (a -> a -> Bool) -> [Gen a -> IO ()]+fracLaws :: Fractional a => (a -> a -> Bool) -> [Gen a -> TestSuite] fracLaws eq =- [ law3 "division numerator" $ \a b c -> (a/b)/c == a/(b*c) <| b/=0 && c/=0- , law3 "division denominator" $ \a b c -> a/(b/c) == a*(c/b) <| b/=0 && c/=0- , law1 "zero numerator" $ \a -> 0/a == 0 <| a/=0- , law1 "one numerator" $ \a -> 1/a == recip a <| a/=0- , law1 "one denominator" $ \a -> a/1 == a- , law1 "division is one" $ \a -> a/a == 1 <| a/=0- , law1 "recip double" $ \a -> a == recip (recip a) <| a/=0- , law3 "times division left" $ \a b c -> (a/b)*c == (a*c)/b <| b/=0- , law3 "times division right" $ \a b c -> a*(b/c) == (a*b)/c <| c/=0- , law3 "plus division left" $ \a b c -> (a/b)+c == (a+c*b)/b <| b/=0- , law3 "plus division right" $ \a b c -> a+(b/c) == (a*c+b)/c <| c/=0- , law3 "minus division left" $ \a b c -> (a/b)-c == (a-c*b)/b <| b/=0- , law3 "minus division right" $ \a b c -> a-(b/c) == (a*c-b)/c <| c/=0- , law2 "negate numerator" $ \a b -> a/(-b) == -(a/b) <| b/=0- , law2 "negate denominator" $ \a b -> (-a)/b == -(a/b) <| b/=0- , law2 "recip times" $ \a b -> recip (a*b) == recip a*recip b <| a/=0 && b/=0- , law2 "recip division" $ \a b -> recip (a/b) == b/a <| a/=0 && b/=0+ [ law3 "division numerator" $ \a b c -> (a/b)/c === a/(b*c) <| b/=0 && c/=0+ , law3 "division denominator" $ \a b c -> a/(b/c) === a*(c/b) <| b/=0 && c/=0+ , law1 "zero numerator" $ \a -> 0/a === 0 <| a/=0+ , law1 "one numerator" $ \a -> 1/a === recip a <| a/=0+ , law1 "one denominator" $ \a -> a/1 === a+ , law1 "division is one" $ \a -> a/a === 1 <| a/=0+ , law1 "recip double" $ \a -> a === recip (recip a) <| a/=0+ , law3 "times division left" $ \a b c -> (a/b)*c === (a*c)/b <| b/=0+ , law3 "times division right" $ \a b c -> a*(b/c) === (a*b)/c <| c/=0+ , law3 "plus division left" $ \a b c -> (a/b)+c === (a+c*b)/b <| b/=0+ , law3 "plus division right" $ \a b c -> a+(b/c) === (a*c+b)/c <| c/=0+ , law3 "minus division left" $ \a b c -> (a/b)-c === (a-c*b)/b <| b/=0+ , law3 "minus division right" $ \a b c -> a-(b/c) === (a*c-b)/c <| c/=0+ , law2 "negate numerator" $ \a b -> a/(-b) === -(a/b) <| b/=0+ , law2 "negate denominator" $ \a b -> (-a)/b === -(a/b) <| b/=0+ , law2 "recip times" $ \a b -> recip (a*b) === recip a*recip b <| a/=0 && b/=0+ , law2 "recip division" $ \a b -> recip (a/b) === b/a <| a/=0 && b/=0 ] where- infix 4 ==- a == b = property (a `eq` b)+ infix 4 ===+ a === b = property (a `eq` b) infix 1 <| p <| b = b ==> p -- local helper-functions-report :: String -> Property -> IO ()-report s p = putStr (take 30 ("- " ++ s ++ repeat ' ')) >> quickCheck p--law1 :: Show a => String -> (a -> Property) -> Gen a -> IO ()-law1 s p g = report s (make g id p)+law1 :: Show a => String -> (a -> Property) -> Gen a -> TestSuite+law1 s p g = addProperty s (make g id p) -law2 :: Show a => String -> (a -> a -> Property) -> Gen a -> IO ()-law2 s p g = report s (make g (make g id) p)+law2 :: Show a => String -> (a -> a -> Property) -> Gen a -> TestSuite+law2 s p g = addProperty s (make g (make g id) p) -law3 :: Show a => String -> (a -> a -> a -> Property) -> Gen a -> IO ()-law3 s p g = report s (make g (make g (make g id)) p)+law3 :: Show a => String -> (a -> a -> a -> Property) -> Gen a -> TestSuite+law3 s p g = addProperty s (make g (make g (make g id)) p) +make :: Show a => Gen a -> (b -> Property) -> (a -> b) -> Property make g c p = forAll g (c . p)
src/Domain/Math/Numeric/Rules.hs view
@@ -20,9 +20,12 @@ ------------------------------------------------------------ -- Rules +alg :: String+alg = "algebra.manipulation"+ calcRuleName :: String -> String -> String calcRuleName opName viewName =- "calculate " ++ opName ++ " [" ++ viewName ++ "]"+ "arithmetic.operation." ++ viewName ++ "." ++ opName calcBinRule :: String -> (a -> a -> a) -> (e -> Maybe (e, e)) -> String -> View e a -> Rule e calcBinRule opName op m viewName v = @@ -52,87 +55,87 @@ return (build v d) negateZero :: Rule Expr -negateZero = makeSimpleRule "negate zero" f+negateZero = makeSimpleRule (alg, "negate-zero") f where f (Negate (Nat n)) | n == 0 = Just 0 f _ = Nothing doubleNegate :: Rule Expr -doubleNegate = makeSimpleRule "double negate" f+doubleNegate = makeSimpleRule (alg, "double-negate") f where f (Negate (Negate a)) = Just a f _ = Nothing plusNegateLeft :: Rule Expr-plusNegateLeft = makeSimpleRule "plus negate left" f+plusNegateLeft = makeSimpleRule (alg, "plus-negate-left") f where f (Negate a :+: b) = Just (b :-: a) f _ = Nothing plusNegateRight :: Rule Expr-plusNegateRight = makeSimpleRule "plus negate right" f+plusNegateRight = makeSimpleRule (alg, "plus-negate-right") f where f (a :+: Negate b) = Just (a :-: b) f _ = Nothing minusNegateLeft :: Rule Expr-minusNegateLeft = makeSimpleRule "minus negate left" f+minusNegateLeft = makeSimpleRule (alg, "minus-negate-left") f where f (Negate a :-: b) = Just (Negate (a :+: b)) f _ = Nothing minusNegateRight :: Rule Expr-minusNegateRight = makeSimpleRule "minus negate right" f+minusNegateRight = makeSimpleRule (alg, "minus-negate-right") f where f (a :-: Negate b) = Just (a :+: b) f _ = Nothing timesNegateLeft :: Rule Expr-timesNegateLeft = makeSimpleRule "times negate left" f+timesNegateLeft = makeSimpleRule (alg, "times-negate-left") f where f (Negate a :*: b) = Just (Negate (a :*: b)) f _ = Nothing timesNegateRight :: Rule Expr-timesNegateRight = makeSimpleRule "times negate right" f+timesNegateRight = makeSimpleRule (alg, "times-negate-right") f where f (a :*: Negate b) = Just (Negate (a :*: b)) f _ = Nothing divisionNegateLeft :: Rule Expr-divisionNegateLeft = makeSimpleRule "division negate left" f+divisionNegateLeft = makeSimpleRule (alg, "division-negate-left") f where f (Negate a :/: b) = Just (Negate (a :/: b)) f _ = Nothing divisionNegateRight :: Rule Expr-divisionNegateRight = makeSimpleRule "division negate right" f+divisionNegateRight = makeSimpleRule (alg, "division-negate-right") f where f (a :/: Negate b) = Just (Negate (a :/: b)) f _ = Nothing divisionNumerator :: Rule Expr-divisionNumerator = makeSimpleRule "division numerator" f+divisionNumerator = makeSimpleRule (alg, "division-numerator") f where f ((a :/: b) :/: c) = Just (a :/: (b :*: c)) f (Negate (a :/: b) :/: c) = Just (Negate (a :/: (b :*: c))) f _ = Nothing divisionDenominator :: Rule Expr-divisionDenominator = makeSimpleRule "division denominator" f+divisionDenominator = makeSimpleRule (alg, "division-denominator") f where f (a :/: (b :/: c)) = Just ((a :*: c) :/: b) f (a :/: Negate (b :/: c)) = Just (Negate ((a :*: c) :/: b)) f _ = Nothing simplerFraction :: Rule Expr-simplerFraction = makeSimpleRule "simpler fraction" $ \expr -> do+simplerFraction = makeSimpleRule (alg, "simpler-fraction") $ \expr -> do new <- canonical rationalRelaxedForm expr guard (expr /= new) return new fractionPlus :: Rule Expr -- also minus-fractionPlus = makeSimpleRule "fraction plus" $ \expr -> do+fractionPlus = makeSimpleRule (alg, "fraction-plus") $ \expr -> do (e1, e2) <- match plusView expr (a, b) <- match fractionForm e1 (c, d) <- match fractionForm e2@@ -140,11 +143,11 @@ return (build fractionForm (a+c, b)) fractionPlusScale :: Rule Expr -- also minus-fractionPlusScale = makeSimpleRuleList "fraction plus scale" $ \expr -> do+fractionPlusScale = makeSimpleRuleList (alg, "fraction-plus-scale") $ \expr -> do (e1, e2) <- matchM plusView expr (a, b) <- (matchM fractionForm e1 `mplus` liftM (\n -> (n, 1)) (matchM integerNormalForm e1)) (c, d) <- (matchM fractionForm e2 `mplus` liftM (\n -> (n, 1)) (matchM integerNormalForm e2))- guard (b /= 0 && d /= 0)+ guard (b /= 0 && d /= 0 && b /= d) let bd = lcm b d e1n = build fractionForm (a * (bd `div` b), bd) e2n = build fractionForm (c * (bd `div` d), bd)@@ -152,7 +155,7 @@ build plusView (e1, e2n) | d /= bd ] fractionTimes :: Rule Expr-fractionTimes = makeSimpleRule "fraction times" f +fractionTimes = makeSimpleRule (alg, "fraction-times") f where f (e1 :*: e2) = do (a, b) <- (matchM fractionForm e1 `mplus` liftM (\n -> (n, 1)) (matchM integerNormalForm e1))
src/Domain/Math/Numeric/Strategies.hs view
@@ -12,109 +12,69 @@ module Domain.Math.Numeric.Strategies ( naturalStrategy, integerStrategy , rationalStrategy, fractionStrategy- , testAll ) where -import Common.Apply+import Common.Context import Common.Strategy-import Common.Transformation-import Common.Uniplate import Common.View import Domain.Math.Expr import Domain.Math.Numeric.Rules import Domain.Math.Numeric.Views-import Domain.Math.Numeric.Generators import Prelude hiding (repeat)-import Test.QuickCheck hiding (label) ------------------------------------------------------------ -- Strategies -naturalStrategy :: LabeledStrategy Expr-naturalStrategy = label "simplify" $ repeat $ alternatives $ map swRule- [ calcPlusWith "nat" natView- , calcMinusWith "nat" natView- , calcTimesWith "nat" natView- , calcDivisionWith "nat" natView- , doubleNegate- , negateZero- , plusNegateLeft- , plusNegateRight- , minusNegateLeft- , minusNegateRight- , timesNegateLeft- , timesNegateRight - , divisionNegateLeft- , divisionNegateRight - ]+naturalStrategy :: LabeledStrategy (Context Expr)+naturalStrategy = label "simplify" $ + repeat $ somewhere $ alternatives $ map use+ [ calcPlusWith "natural" natView+ , calcMinusWith "natural" natView+ , calcTimesWith "natural" natView+ , calcDivisionWith "natural" natView+ , doubleNegate, negateZero, plusNegateLeft, plusNegateRight+ , minusNegateLeft, minusNegateRight, timesNegateLeft+ , timesNegateRight, divisionNegateLeft, divisionNegateRight + ] where natView = makeView f fromInteger where f (Nat n) = Just n f _ = Nothing -integerStrategy :: LabeledStrategy Expr-integerStrategy = label "simplify" $ repeat $ alternatives $ map swRule- [ calcPlusWith "int" integerNormalForm- , calcMinusWith "int" integerNormalForm- , calcTimesWith "int" integerNormalForm- , calcDivisionWith "int" integerNormalForm- , doubleNegate- , negateZero- ]--rationalStrategy :: LabeledStrategy Expr-rationalStrategy = label "simplify" $ repeat $ alternatives $ map swRule- [ calcPlusWith "rational" rationalRelaxedForm- , calcMinusWith "rational" rationalRelaxedForm- , calcTimesWith "rational" rationalRelaxedForm- , calcDivisionWith "int" integerNormalForm- , doubleNegate- , negateZero- , divisionDenominator- , divisionNumerator- , simplerFraction- ]--fractionStrategy :: LabeledStrategy Expr-fractionStrategy = label "simplify" $ repeat $ alternatives $ map swRule- [ fractionPlus, fractionPlusScale, fractionTimes- , calcPlusWith "int" integerNormalForm- , calcMinusWith "int" integerNormalForm- , calcTimesWith "int" integerNormalForm -- not needed?- , calcDivisionWith "int" integerNormalForm- , doubleNegate- , negateZero- , divisionDenominator - , divisionNumerator - , simplerFraction -- only apply when fractionPlusScale is not applicable- ]--swRule :: Uniplate a => Rule a -> Rule a-swRule r = makeSimpleRuleList (name r) (somewhereM (applyAll r))----------------------------------------------------------------- Test code+integerStrategy :: LabeledStrategy (Context Expr)+integerStrategy = label "simplify" $ + repeat $ somewhere $ alternatives $ map use+ [ calcPlusWith "integer" integerNormalForm+ , calcMinusWith "integer" integerNormalForm+ , calcTimesWith "integer" integerNormalForm+ , calcDivisionWith "integer" integerNormalForm+ , doubleNegate, negateZero+ ] -testAll :: IO ()-testAll = sequence_ [test1, test2, test3, test4]+rationalStrategy :: LabeledStrategy (Context Expr)+rationalStrategy = label "simplify" $ + repeat $ somewhere $ alternatives $ map use+ [ calcPlusWith "rational" rationalRelaxedForm+ , calcMinusWith "rational" rationalRelaxedForm+ , calcTimesWith "rational" rationalRelaxedForm+ , calcDivisionWith "integer" integerNormalForm+ , doubleNegate, negateZero, divisionDenominator+ , divisionNumerator, simplerFraction+ ] -test1 = quickCheck $ forAll (sized integerGenerator) $ \e -> - Prelude.not (e `belongsTo` integerView) || - applyD naturalStrategy e `belongsTo` integerNormalForm- -test2 = quickCheck $ forAll (sized integerGenerator) $ \e -> - Prelude.not (e `belongsTo` integerView) || - applyD integerStrategy e `belongsTo` integerNormalForm- -test3 = quickCheck $ forAll (sized rationalGenerator) $ \e -> - Prelude.not (e `belongsTo` rationalView) || - applyD rationalStrategy e `belongsTo` rationalNormalForm- -test4 = quickCheck $ forAll (sized rationalGenerator) $ \e -> - Prelude.not (e `belongsTo` rationalView) || - applyD fractionStrategy e `belongsTo` rationalNormalForm- -{- testC = quickCheck $ forAll (sized rationalGenerator) $ \e -> - let a = cleanUp e- in a == cleanUp a -}+fractionStrategy :: LabeledStrategy (Context Expr)+fractionStrategy = label "simplify" $ + repeat $ + somewhere + ( use (calcPlusWith "integer" integerNormalForm)+ <|> use (calcMinusWith "integer" integerNormalForm)+ <|> use (calcTimesWith "integer" integerNormalForm) -- not needed?+ -- <|> use (calcDivisionWith "integer" integerNormalForm) -- not needed?+ ) |> + somewhere+ ( use doubleNegate <|> use negateZero <|> use divisionDenominator + <|> use fractionPlus <|> use fractionTimes <|> use divisionNumerator+ ) |>+ somewhere (use fractionPlusScale) |>+ somewhere (use simplerFraction)
src/Domain/Math/Numeric/Tests.hs view
@@ -11,59 +11,65 @@ ----------------------------------------------------------------------------- module Domain.Math.Numeric.Tests (main) where -import Common.Apply+import Common.Classes+import Common.Context+import Common.TestSuite import Common.View import Control.Monad+import Data.Maybe import Domain.Math.Expr import Domain.Math.Numeric.Generators import Domain.Math.Numeric.Strategies import Domain.Math.Numeric.Views import Test.QuickCheck -main :: IO ()-main = do- putStrLn "** Correctness numeric views"- let f v = forM_ numGenerators $ \g -> do- quickCheck $ propIdempotence g v- quickCheck $ propSoundness semEqDouble g v- f integerView- f rationalView- f integerNormalForm- f rationalNormalForm- f rationalRelaxedForm- - putStrLn "** Normal forms"- let f v = forM_ numGenerators $ \g ->- quickCheck $ propNormalForm g v- f integerNormalForm+main :: TestSuite+main = suite "Numeric tests" $ do++ suite "Correctness numeric views" $ do+ let f s v = forM_ numGenerators $ \g -> do+ addProperty ("idempotence " ++ s) $ propIdempotence g v+ addProperty ("soundness " ++ s) $ propSoundness semEqDouble g v+ f "integer view" integerView+ f "rational view" rationalView+ f "integer normal form" integerNormalForm+ f "rational normal form" rationalNormalForm+ f "rational relaxed form" rationalRelaxedForm++ suite "Normal forms" $ do+ let f s v = forM_ numGenerators $ \g ->+ addProperty s $ propNormalForm g v+ f "integer normal form" integerNormalForm -- f rationalNormalForm -- no longer a normal form - putStrLn "** Correctness generators"- let f g v = quickCheck $ forAll (sized g) (`belongsTo` v)- f integerGenerator integerView- f rationalGenerator rationalView- f ratioExprGen rationalNormalForm- f ratioExprGenNonZero rationalNormalForm- - putStrLn "** View relations"- let va .>. vb = forM_ numGenerators $ \g -> - quickCheck $ forAll g $ \a -> - not (a `belongsTo` va) || a `belongsTo` vb- integerNormalForm .>. integerView- rationalNormalForm .>. rationalRelaxedForm- rationalRelaxedForm .>. rationalView- integerNormalForm .>. rationalNormalForm- integerView .>. rationalView- - putStrLn "** Pre/post conditions strategies"- let f s pre post = forM_ numGenerators $ \g -> - quickCheck $ forAll g $ \a ->- not (a `belongsTo` pre) || applyD s a `belongsTo` post- f naturalStrategy integerView integerNormalForm- f integerStrategy integerView integerNormalForm- f rationalStrategy rationalView rationalNormalForm- f fractionStrategy rationalView rationalNormalForm- + suite "Correctness generators" $ do+ let f s g v = addProperty s $ forAll (sized g) (`belongsTo` v)+ f "integer" integerGenerator integerView+ f "rational" rationalGenerator rationalView+ f "ratio expr" ratioExprGen rationalNormalForm+ f "ratio expr nonzero" ratioExprGenNonZero rationalNormalForm++ suite "View relations" $ do+ let va .>. vb = forM_ numGenerators $ \g -> + addProperty "" $ forAll g $ \a -> + not (a `belongsTo` va) || a `belongsTo` vb+ integerNormalForm .>. integerView+ rationalNormalForm .>. rationalRelaxedForm+ rationalRelaxedForm .>. rationalView+ integerNormalForm .>. rationalNormalForm+ integerView .>. rationalView++ suite "Pre/post conditions strategies" $ do+ let f l s pre post = forM_ numGenerators $ \g -> + addProperty l $ forAll g $ \a ->+ let run = fromMaybe a . fromContext . applyD s + . newContext emptyEnv . termNavigator+ in not (a `belongsTo` pre) || run a `belongsTo` post+ f "natural" naturalStrategy integerView integerNormalForm+ f "integer" integerStrategy integerView integerNormalForm+ f "rational" rationalStrategy rationalView rationalNormalForm+ f "fraction" fractionStrategy rationalView rationalNormalForm+ numGenerators :: [Gen Expr] numGenerators = map sized [ integerGenerator, rationalGenerator@@ -73,12 +79,11 @@ semEqDouble :: Expr -> Expr -> Bool semEqDouble a b = case (match doubleView a, match doubleView b) of- (Just a, Just b) -> a ~= b+ (Just x, Just y) -> x ~= y (Nothing, Nothing) -> True _ -> False where delta = 0.0001 (~=) :: Double -> Double -> Bool- a ~= b | abs a < delta || abs b < delta = True- | otherwise = abs (1 - (a/b)) < delta+ x ~= y = abs x < delta || abs y < delta || abs (1 - (x/y)) < delta
src/Domain/Math/Numeric/Views.hs view
@@ -10,13 +10,14 @@ -- ----------------------------------------------------------------------------- module Domain.Math.Numeric.Views- ( integralView, realView- , integerView, rationalView, doubleView, mixedFractionView+ ( integralView, integerView+ , rationalView, doubleView, mixedFractionView , integerNormalForm, rationalNormalForm, mixedFractionNormalForm , rationalRelaxedForm, fractionForm , intDiv, fracDiv, exprToNum ) where +import Common.Rewriting import Common.View import Control.Monad import Data.Ratio@@ -26,37 +27,34 @@ -- Numeric views integralView :: Integral a => View Expr a-integralView = makeView (exprToNum f) fromIntegral+integralView = newView "num.integer" (exprToNum f) fromIntegral where f s [x, y] - | s == divideSymbol = + | isDivideSymbol s = intDiv x y- | s == powerSymbol = do+ | isPowerSymbol s = do guard (y >= 0) return (x Prelude.^ y) f _ _ = Nothing+ +integerView :: View Expr Integer+integerView = integralView -realView :: RealFrac a => View Expr a-realView = makeView (exprToNum f) (fromRational . toRational)+rationalView :: View Expr Rational+rationalView = newView "num.rational" (exprToNum f) fromRational where f s [x, y] - | s == divideSymbol = + | isDivideSymbol s = fracDiv x y- | s == powerSymbol = do+ | isPowerSymbol s = do let ry = toRational y guard (denominator ry == 1) let a = x Prelude.^ abs (numerator ry) return (if numerator ry < 0 then 1/a else a) f _ _ = Nothing -integerView :: View Expr Integer-integerView = integralView--rationalView :: View Expr Rational-rationalView = makeView (match realView) fromRational- mixedFractionView :: View Expr Rational-mixedFractionView = makeView (match realView) mix +mixedFractionView = newView "num.mixed-fraction" (match rationalView) mix where mix r = let (d, m) = abs (numerator r) `divMod` denominator r@@ -65,7 +63,7 @@ in sign (fromInteger d .+. rest) doubleView :: View Expr Double-doubleView = makeView rec Number+doubleView = newView "num.double" rec Number where rec expr = case expr of@@ -78,14 +76,14 @@ -- N or -N (where n is a natural number) integerNormalForm :: View Expr Integer-integerNormalForm = makeView (optionNegate f) fromInteger+integerNormalForm = newView "num.integer-nf" (optionNegate f) fromInteger where f (Nat n) = Just n f _ = Nothing -- 5, -(2/5), (-2)/5, but not 2/(-5), 6/8, or -((-2)/5) rationalNormalForm :: View Expr Rational-rationalNormalForm = makeView f fromRational+rationalNormalForm = newView "num.rational-nf" f fromRational where f (Nat a :/: Nat b) = simple a b f (Negate (Nat a :/: Nat b)) = fmap negate (simple a b)@@ -98,7 +96,7 @@ | otherwise = Nothing mixedFractionNormalForm :: View Expr Rational-mixedFractionNormalForm = makeView f fromRational+mixedFractionNormalForm = newView "num.mixed-fraction-nf" f fromRational where f (Negate (Nat a) :-: (Nat b :/: Nat c)) | a > 0 = fmap (negate . (fromInteger a+)) (simple b c) f (Negate (Nat a :+: (Nat b :/: Nat c))) | a > 0 = fmap (negate . (fromInteger a+)) (simple b c)@@ -114,7 +112,7 @@ | otherwise = Nothing fractionForm :: View Expr (Integer, Integer)-fractionForm = makeView f (\(a, b) -> (fromInteger a :/: fromInteger b))+fractionForm = newView "num.fraction-form" f (\(a, b) -> (fromInteger a :/: fromInteger b)) where f (Negate a) = liftM (first negate) (g a) f a = g a@@ -126,7 +124,7 @@ g _ = Nothing rationalRelaxedForm :: View Expr Rational-rationalRelaxedForm = makeView (optionNegate f) fromRational+rationalRelaxedForm = newView "num.rational-relaxed" (optionNegate f) fromRational where f (e1 :/: e2) = do a <- match integerNormalForm e1@@ -145,9 +143,9 @@ doubleSym :: Symbol -> [Double] -> Maybe Double doubleSym s [x, y] - | s == divideSymbol = fracDiv x y- | s == powerSymbol = floatingPower x y - | s == rootSymbol && x >= 0 && y >= 1 = Just (x ** (1/y))+ | isDivideSymbol s = fracDiv x y+ | isPowerSymbol s = floatingPower x y + | isRootSymbol s && x >= 0 && y >= 1 = Just (x ** (1/y)) doubleSym _ _ = Nothing -- General numeric interpretation function: constructors Sqrt and
src/Domain/Math/Polynomial/BuggyRules.hs view
@@ -8,37 +8,426 @@ -- Stability : provisional -- Portability : portable (depends on ghc) ----- Some buggy rules catching common misconceptions on the abc-formula+-- Some buggy rules catching common misconceptions (also on the abc-formula) -- ----------------------------------------------------------------------------- module Domain.Math.Polynomial.BuggyRules where +import Prelude hiding ((^))+import Common.Id import Domain.Math.Expr import Domain.Math.Data.Relation import Domain.Math.Data.OrList import Domain.Math.Polynomial.Views-import Domain.Math.Polynomial.Rules (abcFormula)+import Domain.Math.Polynomial.Rules+import Domain.Math.Polynomial.CleanUp import Domain.Math.Numeric.Views+import Domain.Math.Data.Polynomial+import Domain.Math.Equation.CoverUpRules+import Common.Classes+import Common.Context+import Common.Rewriting import Common.View-import Common.Transformation-import Common.Traversable+import Common.Transformation (Rule, buggyRule, siblingOf, Transformation, useRecognizer, supply1, makeTransList) import Control.Monad+import qualified Common.Transformation as Rule -abcBuggyRules :: [Rule (OrList (Equation Expr))]-abcBuggyRules = map f [ minusB, twoA, minus4AC, oneSolution ]+makeRule :: IsId n => n -> Transformation a -> Rule a+makeSimpleRule :: IsId n => n -> (a -> Maybe a) -> Rule a+makeSimpleRuleList :: IsId n => n -> (a -> [a]) -> Rule a+ruleList :: (RuleBuilder f a, Rewrite a, IsId n) => n -> [f] -> Rule a++makeRule = buggyName Rule.makeRule+makeSimpleRule = buggyName Rule.makeSimpleRule+makeSimpleRuleList = buggyName Rule.makeSimpleRuleList+ruleList = buggyName Rule.ruleList++buggyName :: IsId n => (Id -> a) -> n -> a+buggyName f s = f ("algebra.equations.buggy" # s)++buggyRulesExpr :: [Rule Expr]+buggyRulesExpr = + map (siblingOf distributeTimes)+ [ buggyDistrTimes, buggyDistrTimesForget, buggyDistrTimesSign+ , buggyDistrTimesTooMany, buggyDistrTimesDenom+ ] +++ [ buggyMinusMinus, buggyPriorityTimes -- no sibling defined+ ]++buggyRulesEquation :: [Rule (Equation Expr)]+buggyRulesEquation = + [ buggyPlus, buggyNegateOneSide, siblingOf flipEquation buggyFlipNegateOneSide+ , buggyNegateAll+ , buggyDivNegate, buggyDivNumDenom, buggyCancelMinus+ , buggyMultiplyOneSide, buggyMultiplyForgetOne+ ]++buggyPlus :: Rule (Equation Expr)+buggyPlus = describe "Moving a term from the left-hand side to the \+ \right-hand side (or the other way around), but forgetting to change \+ \the sign." $ + buggyRule $ makeSimpleRuleList "plus" $ \(lhs :==: rhs) -> do+ (a, b) <- matchM plusView lhs+ [ a :==: rhs + b, b :==: rhs + a ]+ `mplus` do+ (a, b) <- matchM plusView rhs+ [ lhs + a :==: b, lhs + b :==: a ]++buggyNegateOneSide :: Rule (Equation Expr)+buggyNegateOneSide = describe "Negate terms on one side only." $+ buggyRule $ makeSimpleRuleList "negate-one-side" $ \(lhs :==: rhs) ->+ [ -lhs :==: rhs, lhs :==: -rhs ] ++buggyFlipNegateOneSide :: Rule (Equation Expr)+buggyFlipNegateOneSide = describe "Negate terms on one side only." $+ buggyRule $ makeSimpleRuleList "flip-negate-one-side" $ \(lhs :==: rhs) ->+ [ -rhs :==: lhs, rhs :==: -lhs ]++buggyNegateAll :: Rule (Equation Expr)+buggyNegateAll = describe "Negating all terms (on both sides of the equation, \+ \but forgetting one term." $+ buggyRule $ makeSimpleRuleList "negate-all" $ \(lhs :==: rhs) -> do + xs <- matchM sumView lhs+ ys <- matchM sumView rhs+ let makeL i = makeEq (zipWith (f i) [0..] xs) (map negate ys)+ makeR i = makeEq (map negate xs) (zipWith (f i) [0..] ys)+ makeEq as bs = build sumView as :==: build sumView bs+ f i j = if i==j then id else negate+ len as = let n = length as in if n < 2 then -1 else n+ map makeL [0 .. len xs] ++ map makeR [0 .. len ys]++buggyDivNegate :: Rule (Equation Expr)+buggyDivNegate = describe "Dividing, but wrong sign." $+ buggyRule $ makeSimpleRuleList "divide-negate" $ \(lhs :==: rhs) -> do+ (a, b) <- matchM timesView lhs+ [ b :==: rhs/(-a) | hasNoVar a ] ++ [ a :==: rhs/(-b) | hasNoVar b ]+ `mplus` do+ (a, b) <- matchM timesView rhs+ [ lhs/(-a) :==: b | hasNoVar a ] ++ [ lhs/(-b) :==: a | hasNoVar b ]++buggyDivNumDenom :: Rule (Equation Expr)+buggyDivNumDenom = describe "Dividing both sides, but swapping \+ \numerator/denominator." $+ buggyRule $ makeSimpleRuleList "divide-numdenom" $ \(lhs :==: rhs) -> do+ (a, b) <- matchM timesView lhs+ [ b :==: a/rhs | hasNoVar rhs ] ++ [ a :==: b/rhs | hasNoVar rhs ]+ `mplus` do+ (a, b) <- matchM timesView rhs+ [ a/lhs :==: b | hasNoVar lhs ] ++ [ b/lhs :==: a | hasNoVar lhs ]++buggyDistrTimes :: Rule Expr+buggyDistrTimes = describe "Incorrect distribution of times over plus: one \+ \term is not multiplied." $+ buggyRule $ makeSimpleRuleList "distr-times-plus" $ \expr -> do+ (a, (b, c)) <- matchM (timesView >>> second plusView) expr+ [ a*b+c, b+a*c ]+ `mplus` do+ ((a, b), c) <- matchM (timesView >>> first plusView) expr+ [ a*c+b, a+b*c ]++buggyDistrTimesForget :: Rule Expr+buggyDistrTimesForget = describe "Incorrect distribution of times over plus: \+ \one term is forgotten." $+ buggyRule $ makeSimpleRuleList "distr-times-plus-forget" $ \expr -> do+ (a, (b, c)) <- matchM (timesView >>> second plusView) expr+ [ a*bn+a*c | bn <- forget b ] ++ [ a*b+a*cn | cn <- forget c ]+ `mplus` do+ ((a, b), c) <- matchM (timesView >>> first plusView) expr+ [ an*c+b*c | an <- forget a] ++ [ a*c+bn*c | bn <- forget b] where- f r = r { ruleSiblings = [name abcFormula] }+ forget :: Expr -> [Expr]+ forget expr =+ case match productView expr of+ Just (b, xs) | n > 1 -> + [ build productView (b, make i) | i <- [0..n-1] ]+ where+ make i = [ x | (j, x) <- zip [0..] xs, i/=j ]+ n = length xs+ _ -> [0] +buggyDistrTimesSign :: Rule Expr+buggyDistrTimesSign = describe "Incorrect distribution of times over plus: \+ \changing sign of addition." $+ buggyRule $ makeSimpleRuleList "distr-times-plus-sign" $ \expr -> do+ (a, (b, c)) <- matchM (timesView >>> second plusView) expr+ [ a.*.b .-. a.*.c ]+ `mplus` do+ ((a, b), c) <- matchM (timesView >>> first plusView) expr+ [ a.*.c .-. b.*.c ]++buggyDistrTimesTooMany :: Rule Expr+buggyDistrTimesTooMany = describe "Strange distribution of times over plus: \+ \a*(b+c)+d, where 'a' is also multiplied to d." $ + buggyRule $ makeSimpleRuleList "distr-times-too-many" $ \expr -> do+ ((a, (b, c)), d) <- matchM (plusView >>> first (timesView >>> second plusView)) expr+ [cleanUpExpr $ a*b+a*c+a*d]++buggyDistrTimesDenom :: Rule Expr+buggyDistrTimesDenom = describe "Incorrct distribution of times over plus: \+ \one of the terms is a fraction, and the outer expression is multiplied by \+ \the fraction's denominator." $+ buggyRule $ makeSimpleRuleList "distr-times-denom" $ \expr -> do+ (a, (b, c)) <- matchM (timesView >>> second plusView) expr+ [(1/a)*b + a*c, a*b + (1/a)*c]+ `mplus` do+ ((a, b), c) <- matchM (timesView >>> first plusView) expr+ [a*(1/c) + b*c, a*c + b*(1/c)]++buggyMinusMinus :: Rule Expr+buggyMinusMinus = describe "Incorrect rewriting of a-(b-c): forgetting to \+ \change sign." $+ buggyRule $ makeSimpleRule "minus-minus" $ \expr ->+ case expr of+ a :-: (b :-: c) -> Just (a-b-c)+ Negate (a :-: b) -> Just (a-b) + _ -> Nothing++buggyCancelMinus :: Rule (Equation Expr)+buggyCancelMinus = describe "Cancel terms on both sides, but terms have \+ \different signs." $+ buggyRule $ makeSimpleRuleList "cancel-minus" $ \(lhs :==: rhs) -> do+ xs <- matchM sumView lhs+ ys <- matchM sumView rhs + [ eq | (i, x) <- zip [0..] xs, (j, y) <- zip [0..] ys+ , cleanUpExpr x == cleanUpExpr (-y) + , let f n as = build sumView $ take n as ++ drop (n+1) as+ , let eq = f i xs :==: f j ys+ ]++buggyPriorityTimes :: Rule Expr+buggyPriorityTimes = describe "Prioity of operators is changed, possibly by \+ \ignoring some parentheses." $+ buggyRule $ makeSimpleRuleList "priority-times" $ \expr -> do+ (a, (b, c)) <- matchM (plusView >>> second timesView) expr+ [(a+b)*c]+ `mplus` do+ ((a, b), c) <- matchM (plusView >>> first timesView) expr+ [a*(b+c)]++buggyMultiplyOneSide :: Rule (Equation Expr)+buggyMultiplyOneSide = describe "Multiplication on one side of the equation only" $+ buggyRule $ makeRule "multiply-one-side" $ + useRecognizer recognizeEq $ supply1 (const (Just 2)) multiplyOneSide+ where+ recognizeEq eq1@(a1 :==: a2) eq2@(b1 :==: b2) =+ let p r = r `notElem` [-1, 0, 1] && + any (myEq eq2) (applyAll (multiplyOneSide r) eq1)+ in maybe False p (recognizeMultiplication a1 b1) + || maybe False p (recognizeMultiplication a2 b2)++recognizeMultiplication :: Expr -> Expr -> Maybe Rational+recognizeMultiplication a b = do+ (_, pa) <- match (polyViewWith rationalView) a + (_, pb) <- match (polyViewWith rationalView) b+ let d = coefficient (degree pa) pa+ guard (d /= 0)+ return (coefficient (degree pb) pb / d)+ +multiplyOneSide :: Rational -> Transformation (Equation Expr)+multiplyOneSide r = makeTransList $ \(lhs :==: rhs) -> do+ xs <- matchM sumView lhs+ ys <- matchM sumView rhs+ let f = map (*fromRational r)+ [build sumView (f xs) :==: rhs, lhs :==: build sumView (f ys)]++buggyMultiplyForgetOne :: Rule (Equation Expr)+buggyMultiplyForgetOne = describe "Multiply the terms on both sides of the \+ \equation, but forget one." $+ buggyRule $ makeRule "multiply-forget-one" $ + useRecognizer recognizeEq $ supply1 (const (Just 2)) multiplyForgetOne+ where+ recognizeEq eq1@(a1 :==: a2) eq2@(b1 :==: b2) =+ let p r = r `notElem` [-1, 0, 1] && + any (myEq eq2) (applyAll (multiplyForgetOne r) eq1)+ in maybe False p (recognizeMultiplication a1 b1) + || maybe False p (recognizeMultiplication a2 b2)++multiplyForgetOne :: Rational -> Transformation (Equation Expr)+multiplyForgetOne r = makeTransList $ \(lhs :==: rhs) -> do+ xs <- matchM sumView lhs+ ys <- matchM sumView rhs+ let makeL i = f (zipWith (mul . (/=i)) [0..] xs) (map (mul True) ys)+ makeR i = f (map (mul True) xs) (zipWith (mul . (/=i)) [0..] ys) + f as bs = build sumView as :==: build sumView bs+ mul b = if b then (*fromRational r) else id+ do guard (length xs > 1) + map makeL [0 .. length xs-1]+ `mplus` do+ guard (length ys > 1)+ map makeR [0 .. length ys-1]++-- Redundant function; should come from exercise+myEq :: Equation Expr -> Equation Expr -> Bool+myEq = let eqR f x y = fmap f x == fmap f y in eqR (acExpr . cleanUpExpr)++---------------------------------------------------------+-- Quadratic and Higher-Degree Polynomials++buggyQuadratic :: IsTerm a => [Rule (Context a)]+buggyQuadratic =+ map use+ [ buggyCoverUpTimesMul, buggyCoverUpEvenPower+ , buggyCoverUpTimesWithPlus, buggyDivisionByVarBoth+ , buggyDivisionByVarZero+ ] +++ map use+ [ buggyDistributionSquare, buggyDistributionSquareForget+ , buggySquareMultiplication+ ] +++ map use+ [ buggyCoverUpEvenPowerTooEarly, buggyCoverUpEvenPowerForget+ , buggyCoverUpSquareMinus+ ]++buggyCoverUpEvenPower :: Rule (Equation Expr)+buggyCoverUpEvenPower = describe "Covering up an even power, but forgetting \+ \the negative root" $ buggyRule $ siblingOf coverUpPower $+ makeSimpleRuleList "coverup.even-power" $ \(lhs :==: rhs) ->+ make (:==:) lhs rhs ++ make (flip (:==:)) rhs lhs+ where+ make equals ab c = do + (a, b) <- isBinary powerSymbol ab+ n <- matchM integerView b+ guard (n > 0 && even n)+ return (a `equals` root c (fromInteger n))++buggyCoverUpEvenPowerTooEarly :: Rule (OrList (Equation Expr))+buggyCoverUpEvenPowerTooEarly = describe "Trying to cover up an even power, \+ \but there is some other operation to be done first. Example: x^2+1=9" $+ buggyRule $ siblingOf coverUpPower $ + makeSimpleRuleList "coverup.even-power-too-early" $ + oneDisjunct $ helperBuggyCUPower True++buggyCoverUpEvenPowerForget :: Rule (OrList (Equation Expr))+buggyCoverUpEvenPowerForget = describe "Trying to cover up an even power, \+ \but there is some other operation to be done first. Example: 9*x^2=81, \+ \ and rewriting this into x=9 or x=-9." $+ buggyRule $ siblingOf coverUpPower $ + makeSimpleRuleList "coverup.even-power-forget" $ + oneDisjunct $ helperBuggyCUPower False++helperBuggyCUPower :: Bool -> Equation Expr -> [OrList (Equation Expr)]+helperBuggyCUPower mode (lhs :==: rhs) =+ make (:==:) lhs rhs ++ make (flip (:==:)) rhs lhs+ where+ make equals ab c = do+ (sym, xs) <- getFunction ab+ (i, x) <- zip [0..] xs+ (a, b) <- isBinary powerSymbol x+ n <- matchM integerView b+ guard (n > 0 && even n)+ let opa | mode = function sym (take i xs ++ [a] ++ drop (i+1) xs)+ | otherwise = a+ rb = root c (fromInteger n)+ return $ orList [opa `equals` rb, opa `equals` (-rb)]++buggyCoverUpTimesMul :: Rule (Equation Expr)+buggyCoverUpTimesMul = describe "Covering-up a multiplication, but instead of \+ \dividing the right-hand side, multiplication is used." $+ buggyRule $ siblingOf coverUpTimes $ + makeSimpleRuleList "coverup.times-mul" $ \(lhs :==: rhs) -> do+ guard (rhs /= 0)+ (a, b) <- isTimes lhs+ [a :==: rhs*b, b :==: rhs*a]+ `mplus` do+ guard (lhs /= 0)+ (a, b) <- isTimes rhs+ [lhs*a :==: b, lhs*b :==: a]++buggyDistributionSquare :: Rule Expr+buggyDistributionSquare = describe "Incorrect removal of parentheses in a squared \+ \addition: forgetting the 2ab term" $ + buggyRule $ siblingOf distributionSquare $+ ruleList "distr-square"+ [ \a b -> (a+b)^2 :~> a^2+b^2+ , \a b -> (a-b)^2 :~> a^2-b^2+ , \a b -> (a-b)^2 :~> a^2+b^2+ ]++buggyDistributionSquareForget :: Rule Expr+buggyDistributionSquareForget = describe "Incorrect removal of parentheses in a squared \+ \addition: squaring only one term" $ + buggyRule $ siblingOf distributionSquare $+ ruleList "distr-square-forget"+ [ \a b -> (a+b)^2 :~> a^2+b+ , \a b -> (a+b)^2 :~> a+b^2+ , \a b -> (a-b)^2 :~> a^2-b+ , \a b -> (a-b)^2 :~> a-b^2+ ]++buggySquareMultiplication :: Rule Expr+buggySquareMultiplication = describe "Incorrect square of a term that involves \+ \a multiplication." $ buggyRule $+ ruleList "square-multiplication"+ [ \a b -> (a*b)^2 :~> a*b^2+ , \a b -> (a*b)^2 :~> a^2*b+ , \a b -> a*b^2 :~> (a*b)^2+ , \a b -> a^2*b :~> (a*b)^2+ ] ++buggyCoverUpSquareMinus :: Rule (OrList (Equation Expr))+buggyCoverUpSquareMinus = describe "A squared term is equal to a negative term \+ \on the right-hand side, resulting in an error in the signs" $+ buggyRule $ makeSimpleRule "coverup.square-minus" $ oneDisjunct $ \eq -> + case eq of+ Sym s [a, 2] :==: b | isPowerSymbol s -> + Just $ orList [a :==: sqrt b, a :==: sqrt (-b)]+ _ -> Nothing++buggyCoverUpTimesWithPlus :: Rule (Equation Expr)+buggyCoverUpTimesWithPlus = describe "Covering-up a multiplication, with an \+ \addition on the other side. Only one of the terms is divided." $ + buggyRule $ makeSimpleRuleList "coverup.times-with-plus" $ + \(lhs :==: rhs) -> make (:==:) lhs rhs ++ make (flip (:==:)) rhs lhs+ where+ make equals ab cd = do+ (a, b) <- isTimes ab+ (c, d) <- isPlus cd+ [ a `equals` (c/b+d), a `equals` (c+d/b), + b `equals` (c/a+d), b `equals` (c+d/a) ]+ +buggyDivisionByVarBoth :: Rule (Equation Expr)+buggyDivisionByVarBoth = describe "Divide both sides by variable, without \+ \introducing the x=0 alternative." $ + buggyRule $ makeSimpleRule "division-by-var-both" $ + \(lhs :==: rhs) -> do+ (s1, p1) <- match polyView lhs+ (s2, p2) <- match polyView rhs+ let n = lowestDegree p1 `min` lowestDegree p2+ guard (n > 0 && s1==s2)+ let f p = build polyView (s1, raise (-n) p)+ return (f p1 :==: f p2)++buggyDivisionByVarZero :: Rule (Equation Expr)+buggyDivisionByVarZero = describe "Divide both sides by variable, without \+ \introducing the x=0 alternative." $ + buggyRule $ makeSimpleRuleList "division-by-var-zero" $ + \(lhs :==: rhs) -> do+ guard (rhs == 0)+ (s, p) <- matchM polyView lhs+ let n = lowestDegree p+ guard (n > 0)+ -- Quick fix to do some trivial steps for a linear equation, so that+ -- buggy rules are recognized. + let eq = build polyView (s, raise (-n) p) :==: 0+ eq : applyM coverUpPlus eq++---------------------------------------------------------+-- ABC formula misconceptions++abcBuggyRules :: [Rule (OrList (Equation Expr))]+abcBuggyRules = map (siblingOf abcFormula) [ minusB, twoA, minus4AC, oneSolution ]+ abcMisconception :: (String -> Rational -> Rational -> Rational -> [OrList (Equation Expr)]) -> Transformation (OrList (Equation Expr)) abcMisconception f = makeTransList $ - onceJoinM $ \(lhs :==: rhs) -> do+ oneDisjunct $ \(lhs :==: rhs) -> do guard (rhs == 0) (x, (a, b, c)) <- matchM (polyNormalForm rationalView >>> second quadraticPolyView) lhs f x a b c minusB :: Rule (OrList (Equation Expr))-minusB = buggyRule $ makeRule "abc misconception minus b" $ +minusB = buggyRule $ makeRule "abc.minus-b" $ abcMisconception $ \x a b c -> do let discr = sqrt (fromRational (b*b - 4 * a * c)) f (?) buggy = @@ -50,7 +439,7 @@ twoA :: Rule (OrList (Equation Expr))-twoA = buggyRule $ makeRule "abc misconception two a" $ +twoA = buggyRule $ makeRule "abc.two-a" $ abcMisconception $ \x a b c -> do let discr = sqrt (fromRational (b*b - 4 * a * c)) f (?) buggy = @@ -61,7 +450,7 @@ orList [ f (+) True, f (-) False ]] minus4AC :: Rule (OrList (Equation Expr))-minus4AC = buggyRule $ makeRule "abc misconception minus 4ac" $ +minus4AC = buggyRule $ makeRule "abc.minus-4ac" $ abcMisconception $ \x a b c -> do let discr (?) = sqrt (fromRational ((b*b) ? (4 * a * c))) f (?) buggy = @@ -72,7 +461,7 @@ orList [ f (+) True, f (-) False ]] oneSolution :: Rule (OrList (Equation Expr))-oneSolution = buggyRule $ makeRule "abc misconception one solution" $ +oneSolution = buggyRule $ makeRule "abc.one-solution" $ abcMisconception $ \x a b c -> do let discr = sqrt (fromRational (b*b - 4 * a * c)) f (?) = Var x :==: (-fromRational b ? discr) / (2 * fromRational a)
src/Domain/Math/Polynomial/CleanUp.hs view
@@ -10,23 +10,25 @@ -- ----------------------------------------------------------------------------- module Domain.Math.Polynomial.CleanUp - ( cleanUp, cleanUpRelation, cleanUpExpr, cleanUpExpr2+ ( cleanUpRelations, cleanUpRelation, cleanUpExpr , cleanUpSimple, collectLikeTerms- , normalizeSum, normalizeProduct+ , acExpr, smart ) where +import Common.Utils (fixpoint) import Common.Uniplate import Common.View import Control.Monad import Data.List import Data.Maybe+import Data.Ord import Domain.Math.Data.OrList import Domain.Math.Data.Relation import Domain.Math.Data.SquareRoot (fromSquareRoot) import Domain.Math.Expr import Domain.Math.Numeric.Views-import Domain.Math.Power.Views-import Domain.Math.Simplification (smartConstructors)+import Domain.Math.Power.OldViews+import Domain.Math.Simplification hiding (simplify, simplifyWith) import Domain.Math.SquareRoot.Views import Prelude hiding ((^), recip) import qualified Prelude@@ -44,10 +46,10 @@ f x y | x == 0 = 0 | y == 0 || x <= 0 = root (fromIntegral x) (fromIntegral y)- | a Prelude.^ y == x = fromIntegral a+ | e Prelude.^ y == x = fromIntegral e | otherwise = root (fromIntegral x) (fromIntegral y) where- a = round (fromIntegral x ** (1 / fromIntegral y))+ e = round ((fromIntegral x :: Double) ** (1 / fromIntegral y)) ---------------------------------------------------------------------- -- Expr normalization@@ -55,108 +57,58 @@ collectLikeTerms :: Expr -> Expr collectLikeTerms = simplifyWith f sumView where- f = normalizeSum . map (simplifyWith (second normalizeProduct) productView)--normalizeProduct :: [Expr] -> [Expr]-normalizeProduct ys = f [ (match rationalView y, y) | y <- ys ]- where - f [] = []- f ((Nothing , e):xs) = e:f xs- f ((Just r , _):xs) = - let cs = r : [ c | (Just c, _) <- xs ]- rest = [ x | (Nothing, x) <- xs ]- in build rationalView (product cs):rest--normalizeSum :: [Expr] -> [Expr]-normalizeSum xs = rec [ (Just $ pm 1 x, x) | x <- xs ]- where- pm :: Rational -> Expr -> (Rational, Expr)- pm r (e1 :*: e2) = case (match rationalView e1, match rationalView e2) of- (Just r1, _) -> pm (r*r1) e2- (_, Just r1) -> pm (r*r1) e1- _ -> (r, e1 .*. e2)- pm r (Negate e) = pm (negate r) e- pm r e = case match rationalView e of- Just r1 -> (r*r1, Nat 1)- Nothing -> (r, e)- - rec [] = []- rec ((Nothing, e):xs) = e:rec xs- rec ((Just (r, a), e):xs) = new:rec rest- where- (js, rest) = partition (maybe False ((==a) . snd) . fst) xs- rs = r:map fst (mapMaybe fst js)- new | null js = e- | otherwise = build rationalView (sum rs) .*. a + f = mergeAlikeSum . map (simplifyWith (second mergeAlikeProduct) productView) --------------------------------------------------------------- Testing--{---- List with hard cases-hardCases = map cleanUpExpr $ let x=Var "x" in- [ -1/2*x*(x/1)- , (x/(-3))- , (x/(-3))^2- , (0-x)*(-x)/(-5/2)- , (x/(-1))^2- , (x/(-1))^2-(-7/2)*x/(-1)- , (x^2+0)*3- , -(49/9*x^2+0^2)*(3/16)- , (0*x-(-x^2))*(-3)- , x^2 - x^2- , x^2-x^2-(x+x)*1- , x^2/(16/3)-x^2*(-1/3)-(x+(-26/3)-x^2)*1- , (-7+7*x)^2-(x*0)^2/(-3)- , 1*(x+93)+4- , (1*(x+(-93/5))-(-4+x/19))/8-(x^2-x+(19/2-x)-34/3*(x*(-41/2)))/9- ] -}- ------------------------------------------------------------- -- Cleaning up cleanUpSimple :: Expr -> Expr-cleanUpSimple = transform (f4 . f2 . f1)+cleanUpSimple = fixpoint (transform (f2 . f1)) where- use v = simplifyWith (assoPlus v) sumView- f1 = simplify rationalView- f2 = use identity- f4 = smartConstructors--cleanUpRelation :: OrList (Relation Expr) -> OrList (Relation Expr)-cleanUpRelation = simplifyWith cleanUp (switchView equationView)+ use v = simplifyWith (assocPlus v) sumView+ f1 = use rationalView+ f2 = smartConstructors -cleanUp :: OrList (Equation Expr) -> OrList (Equation Expr)-cleanUp = idempotent . join . fmap (keepEquation . fmap cleanUpExpr)+cleanUpRelations :: OrList (Relation Expr) -> OrList (Relation Expr)+cleanUpRelations = idempotent . join . fmap cleanUpRelation -keepEquation :: Equation Expr -> OrList (Equation Expr)-keepEquation eq@(a :==: b)- | any falsity (universe a ++ universe b) = false- | a == b = true- | otherwise = - case (match rationalView a, match rationalView b) of- (Just r, Just s) - | r == s -> true- | otherwise -> false- _ -> return eq+cleanUpRelation :: Relation Expr -> OrList (Relation Expr)+cleanUpRelation = f . fmap cleanUpBU where+ f rel+ | any falsity (universe a ++ universe b) = false+ | a == b = fromBool (relationType rel `elem` equals)+ | otherwise = + case (match rationalView a, match rationalView b) of+ (Just r, Just s) -> fromBool (eval (relationType rel) r s)+ _ -> return rel+ where+ (a, b) = (leftHandSide rel, rightHandSide rel)++ equals = + [EqualTo, LessThanOrEqualTo, GreaterThanOrEqualTo, Approximately]++ falsity :: Expr -> Bool falsity (Sqrt e) = maybe False (<0) (match rationalView e) falsity (_ :/: e) = maybe False (==0) (match rationalView e) falsity _ = False-+ -- also simplify square roots-cleanUpExpr2 :: Expr -> Expr-cleanUpExpr2 = cleanUpExpr . transform (simplify (squareRootViewWith rationalView))- cleanUpExpr :: Expr -> Expr-cleanUpExpr = cleanUpBU2 {- e = if a1==a2 && a2==a3 && a3==a3 && a3==a4 then a1 else error $ "\n\n\n" ++ unlines (map show- [e, a1, a2, a3, a4])- where- a1 = cleanUpFix e- a2 = cleanUpBU e- a3 = cleanUpBU2 e- a4 = cleanUpLattice e -}- +cleanUpExpr = fixpoint $ + cleanUpBU . transform (simplify (squareRootViewWith rationalView))++-- normalize expr with associativity and commutative rules for + and *+acExpr :: Expr -> Expr+acExpr expr = + case (match sumView expr, match productView expr) of+ (Just xs, _) | length xs > 1 -> + build sumView $ sort $ map acExpr xs+ (_, Just (b, xs)) | length xs > 1 -> + build productView (b, sort $ map acExpr xs)+ _ -> + descend acExpr expr+ ------------------------------------------------------------ -- Technique 1: fixed points of views {-@@ -170,178 +122,89 @@ f3 = use (powerFactorViewWith rationalView) f4 = smartConstructors -}-assoPlus :: View Expr a -> [Expr] -> [Expr]-assoPlus v = rec . map (simplify v)+assocPlus, assocTimes :: View Expr a -> [Expr] -> [Expr]+assocPlus = assocOp (+)+assocTimes = assocOp (*)++assocOp :: (Expr -> Expr -> Expr) -> View Expr a -> [Expr] -> [Expr]+assocOp op v = rec . map (simplify v) where rec (x:y:zs) =- case canonical v (x+y) of- Just a -> assoPlus v (a:zs)- Nothing -> x:assoPlus v (y:zs)+ case canonical v (op x y) of+ Just a -> rec (a:zs)+ Nothing -> x:rec (y:zs) rec xs = xs --------------------------------------------------------------- Technique 2a: one bottom-up traversal-{-+-- Fixpoint of a bottom-up traversal+ cleanUpBU :: Expr -> Expr-cleanUpBU = transform (f4 . f3 . f2 . f1)+cleanUpBU = {- fixpoint $ -} transform $ \e -> + simplify myView $ + fromMaybe (smart e) $+ canonical rationalView e+ `mplus` do+ a <- canonical specialSqrtOrder e+ -- Just simplify order of terms with square roots for now+ return (transform smart a)+ `mplus` do+ xs <- match sumView e+ guard (length xs > 1)+ return $ build sumView $+ assocPlus myView xs+ `mplus`+ canonical myView e+ `mplus` do+ (b, xs) <- match productView e+ guard (length xs > 1)+ return $ build productView + (b, assocTimes myView xs) where- use v = simplifyWith (assoPlus v) sumView- - f1 = simplify rationalView- f2 = simplify (squareRootViewWith rationalView)- f3 = use (powerFactorViewWith rationalView)- f4 = smartConstructors--}---------------------------------------------------------------- Technique 2b: one bottom-up traversal--cleanUpBU2 :: Expr -> Expr-cleanUpBU2 = transform $ \e -> - case ( canonical rationalView e- , canonical specialSqrtOrder e- , match sumView e- ) of- (Just a, _, _) -> a- (_, Just a, _) -> -- Just simplify order of terms with square roots for now- transform smart a- (_, _, Just xs) | length xs > 1 -> - build sumView (assoPlus (powerFactorViewWith rationalView) xs)- _ -> case canonical (powerFactorViewWith rationalView) e of- Just a -> a- Nothing -> smart e+ myView = powerFactorViewWith rationalView specialSqrtOrder :: View Expr [Expr] specialSqrtOrder = sumView >>> makeView f id where make = match (squareRootViewWith rationalView)- cmp (_, x) (_, y) = g x `compare` g y- g = isNothing . fromSquareRoot+ g = isNothing . fromSquareRoot . snd f xs = do ys <- mapM make xs- return $ map fst $ sortBy cmp $ zip xs ys+ return $ map fst $ sortBy (comparing g) $ zip xs ys smart :: Expr -> Expr smart (a :*: b) = a .*. b smart (a :/: b) = a ./. b smart expr@(Sym s [x, y]) - | s == powerSymbol = x .^. y- | s == rootSymbol = fromMaybe expr $ + | isPowerSymbol s = x .^. y+ | isRootSymbol s = fromMaybe expr $ liftM2 simplerRoot (match rationalView x) (match integerView y) smart (Negate a) = neg a smart (a :+: b) = a .+. b smart (a :-: b) = a .-. b smart (Sqrt (Nat n)) = simplerRoot (fromIntegral n) 2+-- smart (Sqrt a) = maybe (Sqrt a) (`simplerRoot` 2) (match rationalView a) smart e = e ---------------------------------------------------------------- Technique 3: lattice of views-{--data T = R Rational - | S (SquareRoot Rational)- | P String Rational Int- | E Expr deriving Show- -cleanUpLattice :: Expr -> Expr-cleanUpLattice = fromT . toT -fromT :: T -> Expr-fromT (R r) = fromRational r-fromT (S s) = build (squareRootViewWith rationalView) s-fromT (P x r n) = build (powerFactorViewForWith x rationalView) (r, n)-fromT (E e) = e--toT :: Expr -> T-toT (Nat n) = R (fromInteger n)-toT (x :/: y) = divT (toT x) (toT y)-toT (x :*: y) = mulT (toT x) (toT y)-toT (Var x) = P x 1 1-toT (Sym s [x, y]) | s == powerSymbol =- case (toT x, toT y) of- (R x, R y) | denominator y == 1 ->- R (x Prelude.^ fromInteger (numerator y))- (P x a n, R y) | denominator y == 1 -> - P x (a Prelude.^ numerator y) (n*fromInteger (numerator y))- (x, y) -> E (fromT x .^. fromT y)-toT e@(Sqrt _) = fromMaybe (E e) $ do -- Also here, too simplistic- s <- match (squareRootViewWith rationalView) e- return (S s)-toT (Negate e) = negT (toT e)-toT expr =- case match sumView expr of- Just xs | length xs > 1 -> sumT (map toT xs)- _ -> error $ show expr- -negT :: T -> T-negT (R r) = R (negate r)-negT (S s) = S (negate s)-negT (P x r n) = P x (negate r) n-negT (E e) = E (neg e)- -sumT :: [T] -> T-sumT = head . f (const True) . f (`elem` [1,2]) . f (==1) . concatMap g- where- g e@(E a) = case match sumView a of- Just xs | length xs > 1 -> map (upgr . E) xs- _ -> [e]- g a = [a]- - f p (a:b:xs)- | p (orderT a) && p (orderT b) = - f p (plusT a b:xs)- | otherwise = a:f p (b:xs)- f _ xs = xs--plusT :: T -> T -> T-plusT (R 0) t = t -- ?????-plusT t (R 0) = t -- ?????-plusT (R x) (R y) = R (x+y)-plusT (S x) (S y) = S (x+y)-plusT t@(P _ _ _) b = plusT (E $ fromT t) b -plusT (E a) (E b) = E (a .+. b)-plusT a b = convTs plusT a b--divT :: T -> T -> T-divT t (R 1) = t -- ?????-divT t (R (-1)) = negT t -- ?????-divT (R x) (R y) | y /= 0 = R (x/y)-divT t@(R _) b@(R _) = divT (E $ fromT t) b-divT (S x) (S y) = S (x/y)-divT t@(P _ _ _) b = divT (E $ fromT t) b -divT (E a) (E b) = E (a ./. b)-divT a b = convTs divT a b--mulT :: T -> T -> T-mulT (R 0) _ = R 0 -- ?????-mulT _ (R 0) = R 0 -- ?????-mulT t (R 1) = t -- ????-mulT (R 1) t = t -- ?????-mulT (R a) (R b) = R (a*b)-mulT (S a) (S b) = S (a*b)-mulT (P x1 r1 n1) (P x2 r2 n2) | x1==x2 = P x1 (r1*r2) (n1+n2)- | otherwise = error ""-mulT (E a) (E b) = E (a .*. b)-mulT a b = convTs mulT a b--convTs :: (T -> T -> T) -> T -> T -> T-convTs f (R a) t@(S _) = f (S (fromRational a)) t-convTs f (R a) t@(P x _ _) = f (P x (fromRational a) 0) t-convTs f t@(R _) e@(E _) = f (E $ fromT t) e-convTs f t@(P _ _ _) e@(E _) = f (E $ fromT t) e-convTs f a b | orderT a > orderT b = convTs (flip f) b a-convTs _ x y = error $ "conv " ++ show (x, y)--orderT :: T -> Int-orderT (R _) = 1-orderT (S _) = 2-orderT (P _ _ _) = 3-orderT (E _) = 4+------------------------------------------------------------+-- Testing -upgr :: T -> T-upgr (E e) =- case (match (squareRootViewWith rationalView) e, match (powerFactorViewWith rationalView) e) of- (Just a, _) -> upgr (S a)- (_, Just (x, a, n)) -> upgr (P x a n)- _ -> E e-upgr (S a) = maybe (S a) R (fromSquareRoot a)-upgr (P _ a n) | n==0 = R a-upgr t = t -}+{-+-- List with hard cases+hardCases = map cleanUpExpr $ let x=Var "x" in+ [ -1/2*x*(x/1)+ , (x/(-3))+ , (x/(-3))^2+ , (0-x)*(-x)/(-5/2)+ , (x/(-1))^2+ , (x/(-1))^2-(-7/2)*x/(-1)+ , (x^2+0)*3+ , -(49/9*x^2+0^2)*(3/16)+ , (0*x-(-x^2))*(-3)+ , x^2 - x^2+ , x^2-x^2-(x+x)*1+ , x^2/(16/3)-x^2*(-1/3)-(x+(-26/3)-x^2)*1+ , (-7+7*x)^2-(x*0)^2/(-3)+ , 1*(x+93)+4+ , (1*(x+(-93/5))-(-4+x/19))/8-(x^2-x+(19/2-x)-34/3*(x*(-41/2)))/9+ ] -}
src/Domain/Math/Polynomial/Equivalence.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XGeneralizedNewtypeDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -15,27 +15,28 @@ , eqAfterSubstitution ) where +import Common.Classes import Common.Context-import Common.Traversable+import Common.Rewriting+import Common.Uniplate import Common.View+import Control.Monad+import Data.List (sort, nub) import Data.Maybe+import Data.Ord+import Domain.Logic.Formula hiding (Var, disjunctions)+import Domain.Math.Clipboard+import Domain.Math.Data.Interval import Domain.Math.Data.Polynomial hiding (eval)-import Data.List (sort, nub)+import Domain.Math.Data.Relation hiding (eval)+import Domain.Math.Data.SquareRoot+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Polynomial.CleanUp import Domain.Math.Polynomial.Views+import Domain.Math.SquareRoot.Views import Prelude hiding ((^), sqrt)-import Domain.Logic.Formula hiding (Var, disjunctions) import qualified Domain.Logic.Formula as Logic-import Domain.Math.Polynomial.CleanUp-import Domain.Math.Numeric.Views-import Domain.Math.Data.Relation-import Domain.Math.Data.Interval-import Domain.Math.SquareRoot.Views-import Domain.Math.Expr-import Domain.Math.Data.SquareRoot-import Control.Monad-import Domain.Math.Clipboard-import Common.Rewriting hiding (constructor)-import Common.Uniplate relationIntervals :: Ord a => RelationType -> a -> Intervals a relationIntervals relType a = @@ -106,7 +107,7 @@ -- Use normal (numeric) ordering on square roots instance Ord Q where- Q a `compare` Q b = f a `compare` f b + Q a `compare` Q b = comparing f a b where f :: SquareRoot Rational -> Double f = eval . fmap fromRational@@ -120,14 +121,18 @@ highEqContext :: Context (Logic (Relation Expr)) -> Context (Logic (Relation Expr)) -> Bool highEqContext = eqContextWith (polyEq highRel) +eqContextWith :: (Logic (Relation Expr) -> Logic (Relation Expr) -> Bool)+ -> Context (Logic (Relation Expr)) + -> Context (Logic (Relation Expr))+ -> Bool eqContextWith eq a b = isJust $ do termA <- fromContext a termB <- fromContext b guard $ case (ineqOnClipboard a, ineqOnClipboard b) of - (Just a, Just b) -> eq a b && eq termA termB- (Just a, Nothing) -> eq (fmap toEq a) termA && eq a termB- (Nothing, Just b) -> eq (fmap toEq b) termB && eq termA b+ (Just x, Just y) -> eq x y && eq termA termB+ (Just x, Nothing) -> eq (fmap toEq x) termA && eq x termB+ (Nothing, Just y) -> eq (fmap toEq y) termB && eq termA y (Nothing, Nothing) -> eq termA termB where toEq :: Relation Expr -> Relation Expr@@ -151,11 +156,11 @@ cuPlus :: Relation Expr -> Maybe (Relation Expr) cuPlus rel = do (a, b) <- match plusView (leftHandSide rel)- guard (noVars b && noVars (rightHandSide rel))+ guard (hasNoVar b && hasNoVar (rightHandSide rel)) return $ constructor rel a (rightHandSide rel - b) `mplus` do (a, b) <- match plusView (leftHandSide rel)- guard (noVars a && noVars (rightHandSide rel))+ guard (hasNoVar a && hasNoVar (rightHandSide rel)) return $ constructor rel b (rightHandSide rel - a) `mplus` do a <- isNegate (leftHandSide rel)@@ -175,8 +180,8 @@ cuPower rel = do (a, b) <- isBinary powerSymbol (leftHandSide rel) n <- match integerView b- guard (n > 0 && noVars (rightHandSide rel))- let expr = cleanUpExpr2 (root (rightHandSide rel) (fromIntegral n))+ guard (n > 0 && hasNoVar (rightHandSide rel))+ let expr = cleanUpExpr (root (rightHandSide rel) (fromIntegral n)) new = constructor rel a expr opp = constructor (flipSides rel) a (-expr) rt = relationType rel@@ -259,13 +264,13 @@ -- for similarity simLogic :: Ord a => (a -> a) -> Logic a -> Logic a -> Bool-simLogic f a b = rec (fmap f a) (fmap f b)+simLogic f p0 q0 = rec (fmap f p0) (fmap f q0) where rec a b - | isOperator orOperator a =+ | isOr a = let collect = nub . sort . trueOr . collectOr in recList (collect a) (collect b)- | isOperator andOperator a =+ | isAnd a = let collect = nub . sort . falseAnd . collectAnd in recList (collect a) (collect b) | otherwise = @@ -286,6 +291,17 @@ falseAnd xs = if F `elem` xs then [] else xs + shallowEq a b = + let g = descend (const T) + in g a == g b + + isOr (_ :||: _) = True+ isOr _ = False+ + isAnd (_ :||: _) = True+ isAnd _ = False+ + eqAfterSubstitution :: (Functor f, Functor g) => (f (g Expr) -> f (g Expr) -> Bool) -> Context (f (g Expr)) -> Context (f (g Expr)) -> Bool eqAfterSubstitution eq ca cb = fromMaybe False $ do @@ -296,9 +312,7 @@ substitute :: (String, Expr) -> Expr -> Expr substitute (s, a) (Var b) | s==b = a-substitute pair expr = f (map (substitute pair) cs)- where - (cs, f) = uniplate expr+substitute pair expr = descend (substitute pair) expr substOnClipboard :: Context a -> Maybe (String, Expr) substOnClipboard = evalCM $ const $ do
src/Domain/Math/Polynomial/Exercises.hs view
@@ -16,8 +16,8 @@ import Common.Exercise import Common.Rewriting import Common.Strategy-import Common.Traversable-import Common.Transformation+import Common.Classes+import Common.Uniplate import Common.View import Data.Maybe import Domain.Math.Data.OrList@@ -33,6 +33,7 @@ import Domain.Math.Polynomial.Views import Domain.Math.Polynomial.Equivalence import Domain.Math.Numeric.Views+import Domain.Math.Equation.CoverUpRules import Control.Monad ------------------------------------------------------------@@ -40,18 +41,24 @@ linearExercise :: Exercise (Equation Expr) linearExercise = makeExercise - { description = "solve a linear equation"- , exerciseCode = makeCode "math" "lineq"+ { exerciseId = describe "solve a linear equation" $ + newId "algebra.equations.linear" , status = Provisional , parser = parseExprWith (pEquation pExpr)- , similarity = eqRelation cleanUpSimple+ , similarity = eqRelation (acExpr . cleanUpExpr) , equivalence = viewEquivalent linearEquationView , isSuitable = (`belongsTo` linearEquationView) , isReady = solvedRelationWith $ \a -> a `belongsTo` mixedFractionNormalForm || a `belongsTo` rationalNormalForm- , extraRules = liftToContext buggyPlus : linearRules- , strategy = mapRules liftToContext linearStrategy+ , extraRules = map use buggyRulesEquation +++ map use buggyRulesExpr + , ruleOrdering = ruleOrderingWithId+ [ getId coverUpTimes, getId flipEquation+ , getId removeDivision+ ]+ , strategy = linearStrategy+ , navigation = termNavigator , examples = concat (linearEquations ++ [specialCases]) } where@@ -61,87 +68,93 @@ linearMixedExercise :: Exercise (Equation Expr) linearMixedExercise = linearExercise - { description = "solve a linear equation with mixed fractions"- , exerciseCode = makeCode "math" "lineq-mixed"+ { exerciseId = describe "solve a linear equation with mixed fractions" $ + newId "algebra.equations.linear.mixed" , isReady = solvedRelationWith (`belongsTo` mixedFractionNormalForm)- , strategy = mapRules liftToContext linearMixedStrategy+ , strategy = linearMixedStrategy } quadraticExercise :: Exercise (OrList (Relation Expr)) quadraticExercise = makeExercise - { description = "solve a quadratic equation"- , exerciseCode = makeCode "math" "quadreq"+ { exerciseId = describe "solve a quadratic equation" $ + newId "algebra.equations.quadratic" , status = Provisional , parser = \input -> case parseExprWith (pOrList (pEquation pExpr)) input of Left err -> Left err Right xs -> Right (build (switchView equationView) xs)- , similarity = eqOrList cleanUpExpr2+ , similarity = eqOrList cleanUpExpr , equivalence = equivalentRelation (viewEquivalent quadraticEquationsView) , isSuitable = (`belongsTo` (switchView equationView >>> quadraticEquationsView)) , isReady = solvedRelations- , extraRules = map (liftToContext . liftRule (switchView equationView)) $ - quadraticRules ++ abcBuggyRules+ , extraRules = map use abcBuggyRules ++ buggyQuadratic +++ map use buggyRulesEquation ++ map use buggyRulesExpr + , ruleOrdering = ruleOrderingWithId $ + quadraticRuleOrder ++ [getId buggySquareMultiplication] , strategy = quadraticStrategy+ , navigation = termNavigator , examples = map (orList . return . build equationView) (concat quadraticEquations) } higherDegreeExercise :: Exercise (OrList (Relation Expr)) higherDegreeExercise = makeExercise - { description = "solve an equation (higher degree)"- , exerciseCode = makeCode "math" "higherdegree"+ { exerciseId = describe "solve an equation (higher degree)" $+ newId "algebra.equations.polynomial" , status = Provisional , parser = parser quadraticExercise- , similarity = eqOrList cleanUpExpr2+ , similarity = eqOrList cleanUpExpr , eqWithContext = Just $ eqAfterSubstitution $ equivalentRelation (viewEquivalent higherDegreeEquationsView) , isSuitable = (`belongsTo` (switchView equationView >>> higherDegreeEquationsView)) , isReady = solvedRelations- , extraRules = map (liftToContext . liftRule (switchView equationView)) higherDegreeRules+ , extraRules = map use abcBuggyRules ++ buggyQuadratic +++ map use buggyRulesEquation ++ map use buggyRulesExpr + , ruleOrdering = ruleOrderingWithId quadraticRuleOrder , strategy = higherDegreeStrategy+ , navigation = termNavigator , examples = map (orList . return . build equationView) (concat $ higherEq1 ++ higherEq2 ++ [higherDegreeEquations]) } quadraticNoABCExercise :: Exercise (OrList (Relation Expr)) quadraticNoABCExercise = quadraticExercise- { description = "solve a quadratic equation without abc-formula"- , exerciseCode = makeCode "math" "quadreq-no-abc"+ { exerciseId = describe "solve a quadratic equation without abc-formula" $ + newId "algebra.equations.quadratic.no-abc" , status = Alpha , strategy = configure cfg quadraticStrategy } where- cfg = [ (ByName (name prepareSplitSquare), Reinsert)- , (ByName (name bringAToOne), Reinsert)- , (ByName "abc form", Remove)- , (ByName (name simplerPoly), Remove)+ cfg = [ (byName prepareSplitSquare, Reinsert)+ , (byName bringAToOne, Reinsert)+ , (byName (newId "abc form"), Remove)+ , (byName simplerPolynomial, Remove) ] quadraticWithApproximation :: Exercise (OrList (Relation Expr)) quadraticWithApproximation = quadraticExercise- { description = "solve a quadratic equation with approximation"- , exerciseCode = makeCode "math" "quadreq-with-approx"+ { exerciseId = describe "solve a quadratic equation with approximation" $ + newId "algebra.equations.quadratic.approximate" , status = Alpha , parser = parseExprWith (pOrList (pRelation pExpr)) , strategy = configure cfg quadraticStrategy , equivalence = equivalentApprox } where- cfg = [ (ByName "approximate result", Reinsert)- , (ByName "square root simplification", Remove)+ cfg = [ (byName (newId "approximate result"), Reinsert)+ , (byName (newId "square root simplification"), Remove) ] --- fixMe = checksForList quadraticWithApproximation- findFactorsExercise :: Exercise Expr findFactorsExercise = makeExercise- { description = "factorize the expression"- , exerciseCode = makeCode "math" "factor"+ { exerciseId = describe "factorize the expression" $ + newId "algebra.manipulation.polynomial.factor" , status = Provisional , parser = parseExprWith pExpr , similarity = \a b -> cleanUpExpr a == cleanUpExpr b , equivalence = viewEquivalent (polyViewWith rationalView) , isReady = (`belongsTo` linearFactorsView)- , strategy = mapRules liftToContext findFactorsStrategy+ , strategy = findFactorsStrategy+ , navigation = termNavigator+ , extraRules = map liftToContext buggyRulesExpr , examples = concat findFactors } @@ -180,11 +193,12 @@ toEq rel | relationType rel `elem` [EqualTo, Approximately] = Just (leftHandSide rel :==: rightHandSide rel) | otherwise = Nothing- toApprox (a :==: b) =- let f x = case match doubleView x of- Just d -> Number (precision 4 d)- Nothing -> x- in f a .~=. f b++toApprox :: Equation Expr -> Relation Expr+toApprox (a :==: b) = f a .~=. f b+ where+ f x = maybe x (Number . precision 4) (match doubleView x)+ equivalentRelation :: (OrList (Equation a) -> OrList (Equation a) -> Bool) -> OrList (Relation a) -> OrList (Relation a) -> Bool equivalentRelation f ra rb = fromMaybe False $ do@@ -196,32 +210,28 @@ (Expr -> Expr) -> OrList (f Expr) -> OrList (f Expr) -> Bool eqOrList f x y = normOrList f x == normOrList f y -eqRelation :: (Relational f, Eq (f Expr)) => - (Expr -> Expr) -> f Expr -> f Expr -> Bool-eqRelation f x y = normRelation f x == normRelation f y+eqRelation :: (Relational f, Eq (f Expr)) => (Expr -> Expr) -> f Expr -> f Expr -> Bool+eqRelation f x y = fmap f x == fmap f y +-- Normalize the order of disjunctions. Simplify the expression with the function+-- passed as argument, but do not change (flip) the sides of the relation. normOrList :: (Relational f, Ord (f Expr)) => (Expr -> Expr) -> OrList (f Expr) -> OrList (f Expr)-normOrList f = normalize . fmap (normRelation f)--normRelation :: Relational f => (Expr -> Expr) -> f Expr -> f Expr-normRelation f rel- | leftHandSide new > rightHandSide new && isSymmetric new = flipSides new- | otherwise = new- where- new = fmap (normExpr f) rel+normOrList f = normalize . fmap (fmap (normExpr f)) normExpr :: (Expr -> Expr) -> Expr -> Expr-normExpr f = normalizeWith [plusOperator, timesOperator] . f+normExpr f = rec . f where- plusOperator = acOperator (+) isPlus- timesOperator = acOperator (*) isTimes+ plusOperator = makeCommutative $ monoid + (makeBinary (getId plusSymbol) (+) isPlus)+ (simpleConstant "zero" 0)+ timesOperator = makeCommutative $ monoid + (makeBinary (getId timesSymbol) (*) isTimes)+ (simpleConstant "one" 1)+ make = simplifyWith (map rec) . magmaListView --- TODO: move this definition-buggyPlus :: Rule (Equation Expr)-buggyPlus = buggyRule $ makeSimpleRuleList "buggy plus" $ \(lhs :==: rhs) -> do- (a, b) <- matchM plusView lhs- [ a :==: rhs + b, b :==: rhs + a ]- `mplus` do- (a, b) <- matchM plusView rhs- [ lhs + a :==: b, lhs + b :==: a ]+ rec expr = + case expr of+ _ :+: _ -> make plusOperator expr+ _ :*: _ -> make timesOperator expr+ _ -> descend rec expr
src/Domain/Math/Polynomial/IneqExercises.hs view
@@ -15,20 +15,21 @@ import Common.Context import Common.Exercise+import Common.Rewriting import Common.Strategy hiding (not) import Common.Transformation-import Common.Uniplate (uniplate)+import Common.Uniplate (descend) import Common.View import Control.Monad import Data.List (nub, sort) import Data.Maybe (fromMaybe) import Domain.Math.Data.Interval-import Domain.Logic.Formula (Logic((:||:), (:&&:)))+import Domain.Logic.Formula (Logic((:||:), (:&&:)), catLogic) import Domain.Math.Clipboard import Domain.Math.Data.OrList import Domain.Math.Data.Relation import Domain.Math.Equation.CoverUpRules hiding (coverUpPlus)-import Domain.Math.Polynomial.Exercises (eqRelation, normRelation)+import Domain.Math.Polynomial.Exercises (eqRelation, normExpr) import Domain.Math.Equation.Views import Domain.Math.Examples.DWO2 import Domain.Math.Expr@@ -40,17 +41,19 @@ import Domain.Math.SquareRoot.Views import Prelude hiding (repeat) import qualified Domain.Logic.Formula as Logic+import qualified Domain.Logic.Views as Logic ineqLinearExercise :: Exercise (Relation Expr) ineqLinearExercise = makeExercise - { description = "solve a linear inequation"- , exerciseCode = makeCode "math" "linineq"+ { exerciseId = describe "solve a linear inequation" $ + newId "algebra.inequalities.linear" , status = Provisional , parser = parseExprWith (pRelation pExpr) , isReady = solvedRelation , equivalence = linEq- , similarity = eqRelation cleanUpExpr2- , strategy = mapRules liftToContext ineqLinear+ , similarity = eqRelation cleanUpExpr+ , strategy = ineqLinear+ , navigation = termNavigator , examples = let x = Var "x" extra = (x-12) / (-2) :>: (x+3)/3 in map (build inequalityView) (concat ineqLin1 ++ [extra])@@ -58,33 +61,40 @@ ineqQuadraticExercise :: Exercise (Logic (Relation Expr)) ineqQuadraticExercise = makeExercise - { description = "solve a quadratic inequation"- , exerciseCode = makeCode "math" "quadrineq"+ { exerciseId = describe "solve a quadratic inequation" $ + newId "algebra.inequalities.quadratic" , status = Provisional , parser = parseExprWith (pLogicRelation pExpr) , prettyPrinter = showLogicRelation , isReady = solvedRelations , eqWithContext = Just quadrEqContext- , similarity = simLogic (normRelation cleanUpExpr2 . flipGT)+ , similarity = simLogic (fmap (normExpr cleanUpExpr) . flipGT) , strategy = ineqQuadratic+ , navigation = termNavigator+ , ruleOrdering = ruleOrderingWithId quadraticRuleOrder , examples = map (Logic.Var . build inequalityView) (concat $ ineqQuad1 ++ [ineqQuad2, extraIneqQuad]) } ineqHigherDegreeExercise :: Exercise (Logic (Relation Expr)) ineqHigherDegreeExercise = makeExercise - { description = "solve an inequation of higher degree"- , exerciseCode = makeCode "math" "ineqhigherdegree"+ { exerciseId = describe "solve an inequation of higher degree" $ + newId "algebra.inequalities.polynomial" , status = Provisional , parser = parseExprWith (pLogicRelation pExpr) , prettyPrinter = showLogicRelation , isReady = solvedRelations , eqWithContext = Just highEqContext- , similarity = simLogic (normRelation cleanUpExpr2 . flipGT)+ , similarity = simLogic (fmap (normExpr cleanUpExpr) . flipGT) , strategy = ineqHigherDegree+ , navigation = termNavigator+ , ruleOrdering = ruleOrderingWithId quadraticRuleOrder , examples = map (Logic.Var . build inequalityView) ineqHigh } +ineq :: String+ineq = "algebra.inequalities"+ showLogicRelation :: (Eq a, Show a) => Logic (Relation a) -> String showLogicRelation logic = case logic of@@ -107,7 +117,7 @@ ineq2 <- match inequalityView r2 let g (a :>=: b) = b :<=: a g (a :>: b) = b :<: a- g ineq = ineq+ g e = e make (g ineq1) (g ineq2) f _ = Nothing @@ -123,34 +133,42 @@ op _ = False h (x, o1, y, o2, z) = - let f b = if b then (.<=.) else (.<.)- in Logic.Var (f o1 x y) :&&: Logic.Var (f o2 y z)+ let g b = if b then (.<=.) else (.<.)+ in Logic.Var (g o1 x y) :&&: Logic.Var (g o2 y z) +ineqLinear :: LabeledStrategy (Context (Relation Expr))+ineqLinear = cleanUpStrategy (applyTop (fmap cleanUpSimple)) ineqLinearG -ineqLinear :: LabeledStrategy (Relation Expr)-ineqLinear = cleanUpStrategy (fmap cleanUpSimple) $- label "Linear inequation" $- label "Phase 1" (repeat (- removeDivision- <|> ruleMulti (ruleSomewhere distributeTimes)- <|> ruleMulti merge))- <*> - label "Phase 2" (- try varToLeft - <*> try (coverUpPlus id)- <*> try flipSign- <*> try coverUpTimesPositive)+ineqLinearG :: IsTerm a => LabeledStrategy (Context a)+ineqLinearG = label "Linear inequation" $+ label "Phase 1" (repeat + ( use removeDivision+ <|> multi (showId distributeTimes) + (somewhere (useC parentNotNegCheck <*> use distributeTimes))+ <|> multi (showId merge) (once (use merge))+ ))+ <*> + label "Phase 2" + ( try (use varToLeft)+ <*> try coverUpPlus+ <*> try (use flipSign)+ <*> try (use coverUpTimesPositive)+ ) --- helper strategy-coverUpPlus :: (Rule (Relation Expr) -> Rule a) -> Strategy a-coverUpPlus f = alternatives $ map (f . ($ oneVar))- [ coverUpBinaryRule "plus" (commOp . isPlus) (-) - , coverUpBinaryRule "minus left" isMinus (+)- , coverUpBinaryRule "minus right" (flipOp . isMinus) (flip (-))- ] -- [coverUpPlusWith, coverUpMinusLeftWith, coverUpMinusRightWith]+-- helper strategy (todo: fix needed, because the original rules do not +-- work on relations)+coverUpPlus :: IsTerm a => Strategy (Context a) +coverUpPlus = alternatives (map (use . ($ oneVar)) coverUps)+ where+ coverUps :: [ConfigCoverUp -> Rule (Relation Expr)]+ coverUps = + [ coverUpBinaryRule "plus" (commOp . isPlus) (-)+ , coverUpBinaryRule "minus-left" isMinus (+)+ , coverUpBinaryRule "minus-right" (flipOp . isMinus) (flip (-))+ ] coverUpTimesPositive :: Rule (Relation Expr)-coverUpTimesPositive = coverUpBinaryRule "times positive" (commOp . m) (/) varConfig+coverUpTimesPositive = coverUpBinaryRule "times-positive" (commOp . m) (/) configCoverUp where m expr = do (a, b) <- matchM timesView expr@@ -159,7 +177,8 @@ return (a, b) flipSign :: Rule (Relation Expr)-flipSign = makeSimpleRule "flip sign" $ \r -> do+flipSign = describe "Flip sign of inequality" $+ makeSimpleRule (ineq, "flip-sign") $ \r -> do let lhs = leftHandSide r rhs = rightHandSide r guard (isNegative lhs) @@ -170,25 +189,41 @@ maybe False fst (match productView expr) ineqQuadratic :: LabeledStrategy (Context (Logic (Relation Expr)))-ineqQuadratic = label "Quadratic inequality" $ - try (liftRule (contextView (orView >>> justOneView)) turnIntoEquation) - <*> mapRules (liftRule (contextView orView)) quadraticStrategy- <*> solutionInequation+ineqQuadratic = cleanUpStrategy (applyTop cleanUpLogicRelation) $ + label "Quadratic inequality" $ + use trivialRelation+ |> try (useC turnIntoEquation) + <*> quadraticStrategyG+ <*> useC solutionInequation ineqHigherDegree :: LabeledStrategy (Context (Logic (Relation Expr)))-ineqHigherDegree = label "Inequality of a higher degree" $ - try (liftRule (contextView (orView >>> justOneView)) turnIntoEquation) - <*> mapRules (liftRule (contextView orView)) higherDegreeStrategy- <*> solutionInequation+ineqHigherDegree = cleanUpStrategy (applyTop cleanUpLogicRelation) $+ label "Inequality of a higher degree" $ + use trivialRelation+ |> try (useC turnIntoEquation) + <*> higherDegreeStrategyG+ <*> useC solutionInequation -justOneView :: View (OrList a) a-justOneView = makeView (f . disjunctions) return- where- f (Just [r]) = Just r- f _ = Nothing+-- First, cleanup expression. Then, cleanup equations only (there is an +-- explicit rule for the other relations). Finally, simplify the logical+-- proposition (including impotency or).+cleanUpLogicRelation :: Logic (Relation Expr) -> Logic (Relation Expr)+cleanUpLogicRelation = + let f a | relationType a == EqualTo = build orListView (cleanUpRelation a)+ | otherwise = Logic.Var a+ in simplifyWith idempotent orListView . Logic.simplify + . catLogic . fmap (f . fmap cleanUpExpr)+ +trivialRelation :: Rule (OrList (Relation Expr))+trivialRelation =+ makeSimpleRule (ineq, "trivial") $ oneDisjunct $ \a -> do+ let new = cleanUpRelation a+ guard (isTrue new || isFalse new)+ return new turnIntoEquation :: Rule (Context (Relation Expr))-turnIntoEquation = makeSimpleRule "turn into equation" $ withCM $ \r -> do+turnIntoEquation = describe "Turn into equation" $ + makeSimpleRule (ineq, "to-equation") $ withCM $ \r -> do guard (relationType r `elem` ineqTypes) addToClipboard "ineq" (toExpr r) return (leftHandSide r .==. rightHandSide r)@@ -198,19 +233,20 @@ -- Todo: cleanup this function solutionInequation :: Rule (Context (Logic (Relation Expr)))-solutionInequation = makeSimpleRule "solution inequation" $ withCM $ \r -> do- ineq <- lookupClipboard "ineq" >>= fromExpr+solutionInequation = describe "Determine solution for inequality" $ + makeSimpleRule (ineq, "give-solution") $ withCM $ \r -> do+ inEquation <- lookupClipboard "ineq" >>= fromExpr removeClipboard "ineq"- orv <- maybeCM (matchM orView r)+ orv <- maybeCM (matchM orListView r) case disjunctions orv of Nothing -> -- both sides are the same- if relationType ineq `elem` [GreaterThanOrEqualTo, LessThanOrEqualTo]+ if relationType inEquation `elem` [GreaterThanOrEqualTo, LessThanOrEqualTo] then return Logic.T else return Logic.F Just [] -> do -- no solutions found for equations- let vs = collectVars (toExpr ineq)+ let vs = vars (toExpr inEquation) guard (not (null vs))- if evalIneq ineq (head vs) 0+ if evalIneq inEquation (head vs) 0 then return Logic.T else return Logic.F Just xs -> do@@ -220,9 +256,9 @@ ds <- matchM (listView doubleView) zs guard (all (==v) vs) let rs = makeRanges including (sort (zipWith A ds zs))- including = relationType ineq `elem` [GreaterThanOrEqualTo, LessThanOrEqualTo]+ including = relationType inEquation `elem` [GreaterThanOrEqualTo, LessThanOrEqualTo] return $ fromIntervals v fromDExpr $ - fromList [ this | (d, isP, this) <- rs, isP || evalIneq ineq v d ]+ fromList [ this | (d, isP, this) <- rs, isP || evalIneq inEquation v d ] where makeRanges :: Bool -> [DExpr] -> [(Double, Bool, Interval DExpr)] makeRanges b xs =@@ -248,9 +284,9 @@ evalIneq :: Relation Expr -> String -> Double -> Bool evalIneq r v d = fromMaybe False $- liftM2 (evalType (relationType r)) (use leftHandSide) (use rightHandSide)+ liftM2 (evalType (relationType r)) (useSide leftHandSide) (useSide rightHandSide) where- use f = match doubleView (sub (f r))+ useSide f = match doubleView (sub (f r)) evalType tp = case tp of @@ -263,8 +299,7 @@ Approximately -> \a b -> abs (a-b) < 0.001 sub (Var x) | x==v = Number d- sub expr = build (map sub cs)- where (cs, build) = uniplate expr+ sub expr = descend sub expr data DExpr = A Double Expr
+ src/Domain/Math/Polynomial/LeastCommonMultiple.hs view
@@ -0,0 +1,138 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Domain.Math.Polynomial.LeastCommonMultiple + ( lcmExpr, divisionExpr, noCommonFactor, equalFactors, testLCM + , powerProductView+ ) where++import Prelude hiding ((^))+import Common.TestSuite+import Common.View+import Control.Monad+import Data.List+import Data.Ratio+import Data.Maybe+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Power.Views+import Test.QuickCheck++-- | Returns the least common multiple of two expressions. +lcmExpr :: Expr -> Expr -> Expr+lcmExpr a b = fromMaybe (a*b) $ do+ (ar, as) <- match powerProductView a+ (br, bs) <- match powerProductView b+ return $ build powerProductView (lcmR ar br, merge as bs)+ where + lcmR :: Rational -> Rational -> Rational+ lcmR r1 r2 = + let f r = numerator r * denominator r+ in fromIntegral (lcm (f r1) (f r2))+ + merge :: [(Expr, Integer)] -> [(Expr, Integer)] -> [(Expr, Integer)]+ merge = foldr op id+ where+ op (e, n1) f ys = + let n2 = fromMaybe 0 (lookup e ys)+ rest = filter ((/=e) . fst) ys+ in (e, n1 `max` n2) : f rest++-- | Only succeeds if there is no remainder+divisionExpr :: Expr -> Expr -> Maybe Expr+divisionExpr a b = do+ (ar, as) <- match powerProductView a+ (br, bs) <- match powerProductView b+ xs <- as `without` bs+ return $ build powerProductView (ar/br, xs)+ where+ without :: [(Expr, Integer)] -> [(Expr, Integer)] -> Maybe [(Expr, Integer)]+ without [] ys =+ guard (null ys) >> return []+ without ((e,n1):xs) ys = + let n2 = fromMaybe 0 (lookup e ys)+ rest = filter ((/=e) . fst) ys+ in liftM ((e,n1-n2):) (without xs rest)+ +powerProductView :: View Expr (Rational, [(Expr, Integer)])+powerProductView = makeView f g+ where+ f expr = do+ (b, xs) <- match productView expr+ let (r, ys) = collectPairs xs+ return (if b then -r else r, merge ys)+ + g (r, xs) =+ build productView (False, fromRational r : map (build pvn) xs)+ + pvn :: View Expr (Expr, Integer)+ pvn = powerView >>> second integerView++ collectPairs :: [Expr] -> (Rational, [(Expr, Integer)])+ collectPairs = foldr op (1, [])+ where+ op e (r, xs) = + let mr = match rationalView e + h r2 = (r*r2, xs)+ pair = fromMaybe (e,1) (match pvn e)+ in maybe (r, pair:xs) h mr++ merge :: [(Expr, Integer)] -> [(Expr, Integer)]+ merge [] = []+ merge xs@((e, _) : _) = + let (xs1, xs2) = partition ((==e) . fst) xs+ n = sum (map snd xs1) + in (e, n) : merge xs2+ +testLCM :: TestSuite+testLCM = suite "lcmExpr" $ do+ addProperty "transitivity" $ f3 $ \a b c -> + lcmExpr a (lcmExpr b c) ~= lcmExpr (lcmExpr a b) c+ addProperty "commutativity" $ f2 $ \a b -> + lcmExpr a b ~= lcmExpr b a+ addProperty "idempotency" $ f1 $ \a -> + lcmExpr a a ~= absExpr a+ addProperty "zero" $ f1 $ \a -> + lcmExpr a 0 ~= 0+ addProperty "one" $ f1 $ \a -> + lcmExpr a 1 ~= absExpr a+ addProperty "sign" $ f2 $ \a b -> + lcmExpr a b ~= lcmExpr (-a) b+ where + f1 g = liftM g genExpr+ f2 g = liftM2 g genExpr genExpr+ f3 g = liftM3 g genExpr genExpr genExpr+ + genExpr, genTerm, genAtom :: Gen Expr+ genExpr = do+ n <- choose (0, 10)+ b <- arbitrary+ xs <- replicateM n genTerm+ return $ build productView (b, xs)+ + genTerm = frequency [(3, genAtom), (1, liftM fromInteger arbitrary)]+ + genAtom = do+ v <- oneof $ map (return . Var) ["a", "b", "c"]+ i <- choose (-10, 10)+ n <- choose (0, 10)+ p <- frequency [(3, return v), (1, return (v .+. fromInteger i))]+ frequency [(3, return p), (1, return (p^fromInteger n))]++ (~=) = equalFactors+ absExpr = simplifyWith (first (const False)) productView++noCommonFactor :: Expr -> Expr -> Bool+noCommonFactor x y = lcmExpr x y `equalFactors` (x*y)+ +equalFactors :: Expr -> Expr -> Bool+equalFactors x y = f x == f y+ where f = simplifyWith (second sort) powerProductView
+ src/Domain/Math/Polynomial/RationalExercises.hs view
@@ -0,0 +1,313 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Domain.Math.Polynomial.RationalExercises + ( rationalEquationExercise+ , simplifyRationalExercise, divisionRationalExercise+ , eqSimplifyRational+ ) where++import Common.Classes+import Common.Context+import Common.Exercise+import Common.Navigator+import Common.Rewriting+import Common.Strategy hiding (not)+import Common.Uniplate+import Common.Utils (fst3)+import Common.View+import Control.Monad+import Data.List hiding (repeat, replicate)+import Data.Maybe+import Domain.Logic.Formula hiding (disjunctions, Var)+import qualified Domain.Logic as Logic+import qualified Domain.Logic.Views as Logic+import Domain.Logic.Views hiding (simplify)+import Domain.Math.Clipboard+import Domain.Math.Data.OrList+import Domain.Math.Data.Relation+import Domain.Math.Equation.CoverUpRules+import Domain.Math.Equation.Views+import Domain.Math.Examples.DWO4+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Polynomial.CleanUp+import Domain.Math.Polynomial.Exercises (eqOrList)+import Domain.Math.Polynomial.LeastCommonMultiple+import Domain.Math.Polynomial.RationalRules+import Domain.Math.Polynomial.Rules+import Domain.Math.Polynomial.Strategies+import Domain.Math.Polynomial.Views+import Domain.Math.SquareRoot.Views+import Domain.Math.Power.OldViews+import Prelude hiding (repeat, replicate, until, (^))+import qualified Data.Set as S++rationalEquationExercise :: Exercise (OrList (Equation Expr))+rationalEquationExercise = makeExercise + { exerciseId = describe "solve a rational equation (with a variable in a divisor)" $ + newId "algebra.equations.rational"+ , status = Provisional+ , parser = parseExprWith (pOrList (pEquation pExpr))+ , isSuitable = isJust . rationalEquations+ , isReady = solvedRelations+ , eqWithContext = Just eqRationalEquation+ , similarity = eqOrList cleanUpExpr+ , strategy = rationalEquationStrategy+ , ruleOrdering = ruleOrderingWithId quadraticRuleOrder+ , navigation = termNavigator+ , examples = map return (concat brokenEquations)+ }+ +simplifyRationalExercise :: Exercise Expr+simplifyRationalExercise = makeExercise+ { exerciseId = describe "simplify a rational expression (with a variable in a divisor)" $ + newId "algebra.manipulation.rational.simplify"+ , status = Alpha -- Provisional+ , parser = parseExpr+-- isSuitable+ , isReady = simplifiedRational+ -- , eqWithContext = Just eqSimplifyRational+ , similarity = \x y -> cleanUpExpr x == cleanUpExpr y+ , strategy = simplifyRationalStrategy+ , ruleOrdering = ruleOrderingWithId quadraticRuleOrder+ , navigation = termNavigator+ , examples = concat (normBroken ++ normBroken2)+ }+ +divisionRationalExercise :: Exercise Expr+divisionRationalExercise = simplifyRationalExercise+ { exerciseId = describe "divide a rational expression ('uitdelen')" $ + newId "math.divrational"+ , strategy = label "divide broken fraction" succeed+ , examples = concat deelUit+ }++rationalEquationStrategy :: LabeledStrategy (Context (OrList (Equation Expr)))+rationalEquationStrategy = cleanUpStrategy (applyTop (fmap (fmap cleaner))) $+ label "Rational equation" $ + brokenFormToPoly <*> higherDegreeStrategyG <*> checkSolutionStrategy+ where+ -- a custom-made clean-up function. (Standard) cleanUpExpr function + -- has some strange interaction with the rules+ cleaner = transform (simplify (powerFactorViewWith rationalView)) + . cleanUpSimple . transform smart+ + brokenFormToPoly = label "rational form to polynomial" $ until allArePoly $+ ( useC divisionIsZero <|> useC divisionIsOne + <|> useC sameDivisor <|> useC sameDividend+ <|> use coverUpPlus <|> use coverUpMinusLeft <|> use coverUpMinusRight+ <|> use coverUpNegate+ ) |> + ( useC crossMultiply <|> useC multiplyOneDiv )+ checkSolutionStrategy = label "check solutions" $ + try (multi (showId checkSolution) (somewhere checkSolution))++allArePoly :: Context (OrList (Equation Expr)) -> Bool+allArePoly = + let f a = a `belongsTo` polyView+ in maybe False (all f . concatMap crush . crush) . fromContext++simplifyRationalStrategy :: LabeledStrategy (Context Expr)+simplifyRationalStrategy = cleanUpStrategy (applyTop cleaner) $+ label "Simplify rational expression" $+ phaseOneDiv <*> phaseSimplerDiv+ where+ -- a custom-made clean-up function. (Standard) cleanUpExpr function + -- has some strange interaction with the rules+ cleaner = transform (simplify (powerFactorViewWith rationalView)) . cleanUpSimple+ + phaseOneDiv = label "Write as division" $+ until isDivC $ + use fractionPlus <|> use fractionScale <|> use turnIntoFraction+ phaseSimplerDiv = label "Simplify division" $+ repeat $+ (onlyLowerDiv findFactorsStrategyG <|> somewhere (useC cancelTermsDiv)+ <|> commit (onlyUpperDiv (repeat findFactorsStrategyG) <*> useC cancelTermsDiv))+ |> ( somewhere (use merge) + <|> multi (showId distributeTimes) (exceptLowerDiv (use distributeTimes))+ )++isDivC :: Context a -> Bool+isDivC = maybe False (isJust . isDivide :: Term -> Bool) . currentT++-- First check that the whole strategy can be executed. Cleaning up is not +-- propagated correctly to predicate in check combinator, hence the use of+-- cleanUpStrategy (which is not desirable here).+commit :: IsStrategy f => f (Context Expr) -> Strategy (Context Expr)+commit s = let cs = cleanUpStrategy (applyTop cleanUpExpr) (label "" s)+ f a = fromMaybe a (do b <- top a; c <- current a; return (change (const c) b))+ in check (applicable cs . f) <*> s++exceptLowerDiv :: IsStrategy f => f (Context a) -> Strategy (Context a)+exceptLowerDiv = somewhereWith "except-lower-div" $ \a -> + if isDivC a then [1] else [0 .. arity a-1]++onlyUpperDiv :: IsStrategy f => f (Context a) -> Strategy (Context a)+onlyUpperDiv = onceWith "only-upper-div" $ \a -> [ 1 | isDivC a ]+ +onlyLowerDiv :: IsStrategy f => f (Context a) -> Strategy (Context a)+onlyLowerDiv = onceWith "only-lower-div" $ \a -> [ 2 | isDivC a ]+ +simplifiedRational :: Expr -> Bool+simplifiedRational expr =+ case expr of+ Negate a -> simplifiedRational a+ _ -> f expr+ where+ f (a :/: b) = inPolyForm a && noCommonFactor a b && inFactorForm b+ f _ = False++ inPolyForm :: Expr -> Bool+ inPolyForm a =+ a `belongsTo` polyNormalForm identity ||+ S.size (varSet expr) > 1+ + inFactorForm :: Expr -> Bool+ inFactorForm = flip belongsTo $+ let v = first (polyNormalForm identity >>> second linearPolyView)+ in powerProductView >>> second (listView v)++rationalEquations :: OrList (Equation Expr) -> Maybe (OrList Expr)+rationalEquations = maybe (return true) f . disjunctions+ where + f xs = do + yss <- mapM rationalEquation xs+ return (join (orList yss))+ +rationalEquation :: Equation Expr -> Maybe (OrList Expr)+rationalEquation eq = do+ let (lhs :==: rhs) = coverUp eq+ (a, b, c) = rationalExpr (lhs .-. rhs)+ (_, as) <- match productView a+ (_, bs) <- match productView b+ let condition = foldr ((.&&.) . notZero) c bs+ new1 <- match higherDegreeEquationsView $ orList $ map (:==: 0) as+ return (restrictOrList condition new1)++restrictOrList :: Logic (Relation Expr) -> OrList Expr -> OrList Expr+restrictOrList p0 = maybe true (orList . filter p) . disjunctions+ where+ p zeroExpr = + case coverUp (zeroExpr :==: 0) of + Var x :==: a -> -- returns true if a contradiction was not found+ substVar x (cleanUpExpr a) p0 /= F + _ -> True++ substVar x a = Logic.simplify . catLogic . fmap (simpler . fmap (cleanUpExpr . subst))+ where + subst (Var s) | x == s = a+ subst expr = descend subst expr+ + simpler r = fromMaybe (Logic.Var r) $ do+ a <- match (squareRootViewWith rationalView) (leftHandSide r)+ b <- match (squareRootViewWith rationalView) (rightHandSide r)+ case (a==b, relationType r) of+ (True, EqualTo) -> return T+ (False, EqualTo) -> return F + (True, NotEqualTo) -> return F+ (False, NotEqualTo) -> return T+ _ -> Nothing++eqRationalEquation :: Context (OrList (Equation Expr)) -> Context (OrList (Equation Expr)) -> Bool+eqRationalEquation ca cb = fromMaybe False $+ liftM2 (==) (solve ca) (solve cb)+ where+ solve ctx = do + let f = fromMaybe T . conditionOnClipboard+ a <- fromContext ctx + xs <- rationalEquations a+ ys <- disjunctions (restrictOrList (f ctx) xs)+ return (sort (nub ys))+ +eqSimplifyRational :: Context Expr -> Context Expr -> Bool+eqSimplifyRational ca cb = fromMaybe False $ do+ a <- fromContext ca+ b <- fromContext cb+ let a1c = cleanUpExpr (fst3 (rationalExpr a))+ b1c = cleanUpExpr (fst3 (rationalExpr b))+ manyVars = S.size (varSet a `S.union` varSet b) > 1+ if manyVars then return True else do+ p1 <- match (polyViewWith rationalView) a1c+ p2 <- match (polyViewWith rationalView) b1c+ return (manyVars || p1==p2)+ +conditionOnClipboard :: Context a -> Maybe (Logic (Relation Expr))+conditionOnClipboard = evalCM $ const $+ lookupClipboardG "condition"++-- write expression as a/b, under certain conditions+rationalExpr :: Expr -> (Expr, Expr, Logic (Relation Expr))+rationalExpr expr =+ case expr of+ a :+: b -> rationalExpr a `fPlus` rationalExpr b+ a :-: b -> rationalExpr (a :+: Negate b)+ Negate a -> fNeg (rationalExpr a)+ a :*: b -> rationalExpr a `fTimes` rationalExpr b+ a :/: b -> rationalExpr a `fTimes` fRecip (rationalExpr b)+ Sym s [a, b] | isPowerSymbol s -> + fPower (rationalExpr a) b+ _ -> (expr, 1, T)+ where+ fNeg (a, b, p) = (neg a, b, p)+ fRecip (a, b, p) = (b, a, notZero b .&&. p)+ fPower (a, b, p) n = (a .^. n, b .^. n, p)+ fTimes (a1, a2, p) (b1, b2, q) = (a1 .*. b1, a2 .*. b2, p .&&. q)+ fPlus (a1, a2, p) (b1, b2, q) =+ case (divisionExpr c2 a2, divisionExpr c2 b2) of + (Just a3, Just b3) + | a1 == b1 -> (a1 .*. (a3 .+. b3), c2, pq)+ | a1 == neg b1 -> (a1 .*. (a3 .-. b3), c2, pq)+ | otherwise -> (a1 .*. a3 .+. b1 .*. b3, c2, pq)+ _ -> (a1 .*. b2 .+. b1 .*. a2, a2 .*. b2, pq)+ where+ c2 = lcmExpr a2 b2+ pq = p .&&. q++notZero :: Expr -> Logic (Relation Expr)+notZero expr =+ case match rationalView expr of+ Just r | r /= 0 -> T+ | otherwise -> F+ _ -> Logic.Var (expr ./=. 0)++-----------------+-- test code++{-+raar = brokenExpr $ x^2/(5*x+6) + 1+ where x = Var "x"+-+go0 = checkExercise rationalEquationExercise++go = checkExercise simplifyRationalExercise++see n = printDerivation ex (examples ex !! (n-1))+ where ex = --rationalEquationExercise + simplifyRationalExercise+ +go4 = printDerivation findFactorsExercise $ -a + 4+ where x = Var "x"+ a = Var "a"+ +test = e4+ where + a = Var "a"+ b = Var "b"+ + e1 = 6*a*b*a+ e2 = -4*b^2*a*2+ e3 = lcmExpr e1 e2+ e4 = divisionExpr e3 e1+ e5 = divisionExpr e3 e2+ +go = putStrLn $ unlines $ map show $ zip [1..] $ map (brokenEq []) (concat brokenEquations)+-}
+ src/Domain/Math/Polynomial/RationalRules.hs view
@@ -0,0 +1,186 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Domain.Math.Polynomial.RationalRules + ( divisionIsZero, divisionIsOne, sameDivisor, sameDividend+ , crossMultiply, multiplyOneDiv, fractionPlus, cancelTermsDiv+ , fractionScale, turnIntoFraction, checkSolution+ ) where++import Common.Context+import Common.Id+import Common.Transformation+import Common.View+import Control.Monad+import Data.Maybe+import Domain.Logic.Formula hiding (disjunctions, Var)+import Domain.Logic.Views+import Domain.Math.Clipboard+import Domain.Math.Data.OrList+import Domain.Math.Data.Relation+import Domain.Math.Equation.CoverUpRules+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Polynomial.CleanUp+import Domain.Math.Polynomial.LeastCommonMultiple+import Domain.Math.Polynomial.Views+import Domain.Math.Power.Views+import qualified Domain.Logic.Formula as Logic++ratId :: Id+ratId = newId "algebra.equations.rational"++---------------------------------------------------------------+-- Rules for rational expressions and rational equations++-- a/b = 0 iff a=0 (and b/=0)+divisionIsZero :: Rule (Context (Equation Expr))+divisionIsZero = makeSimpleRule (ratId, "division-zero") $ withCM $ \(lhs :==: rhs) -> do+ guard (rhs == 0)+ (a, b) <- matchM divView lhs+ conditionNotZero b+ return (a :==: 0)+ +-- a/b = 1 iff a=b (and b/=0)+divisionIsOne :: Rule (Context (Equation Expr))+divisionIsOne = makeSimpleRule (ratId, "division-one") $ withCM $ \(lhs :==: rhs) -> do+ guard (rhs == 1)+ (a, b) <- matchM divView lhs+ conditionNotZero b+ return (a :==: b)++-- a/c = b/c iff a=b (and c/=0)+sameDivisor :: Rule (Context (Equation Expr))+sameDivisor = makeSimpleRule (ratId, "same-divisor") $ withCM $ \(lhs :==: rhs) -> do+ (a, c1) <- matchM divView lhs+ (b, c2) <- matchM divView rhs+ guard (c1==c2)+ conditionNotZero c1+ return (a :==: b)+ +-- a/b = a/c iff a=0 or b=c (and b/=0 and c/=0)+sameDividend :: Rule (Context (OrList (Equation Expr)))+sameDividend = makeSimpleRule (ratId, "same-dividend") $ withCM $ oneDisjunct $ \(lhs :==: rhs) -> do+ (a1, b) <- matchM divView lhs+ (a2, c) <- matchM divView rhs+ guard (a1==a2)+ conditionNotZero b+ conditionNotZero c+ return $ orList [a1 :==: 0, b :==: c]+ +-- a/b = c/d iff a*d = b*c (and b/=0 and d/=0)+crossMultiply :: Rule (Context (Equation Expr))+crossMultiply = makeSimpleRule (ratId, "cross-multiply") $ withCM $ \(lhs :==: rhs) -> do+ (a, b) <- matchM divView lhs+ (c, d) <- matchM divView rhs+ conditionNotZero b+ conditionNotZero d+ return (a*d :==: b*c)+ +-- a/b = c iff a = b*c (and b/=0)+multiplyOneDiv :: Rule (Context (Equation Expr))+multiplyOneDiv = makeSimpleRule (ratId, "multiply-one-div") $ withCM $ \(lhs :==: rhs) -> + f (:==:) lhs rhs `mplus` f (flip (:==:)) rhs lhs+ where+ f eq ab c = do + guard (not (c `belongsTo` divView))+ (a, b) <- matchM divView ab+ conditionNotZero b+ return (a `eq` (b*c))+ +-- a/c + b/c = a+b/c (also see Numeric.Rules)+fractionPlus :: Rule Expr -- also minus+fractionPlus = makeSimpleRule (ratId, "rational-plus") $ \expr -> do+ ((a, b), (c, d)) <- match myView expr+ guard (b == d)+ return (build divView (a+c, b))+ where+ myView = plusView >>> (divView *** divView)++-- ab/ac => b/c (if a/=0)+-- Note that the common term can be squared (in one of the parts)+cancelTermsDiv :: Rule (Context Expr)+cancelTermsDiv = makeSimpleRule (ratId, "cancel-div") $ withCM $ \expr -> do+ ((b, xs), (c, ys)) <- matchM myView expr+ let (ps, qs, rs) = rec (map f xs) (map f ys)+ guard (not (null rs))+ conditionNotZero (build productView (False, map g rs))+ return $ build myView ((b, map g ps), (c, map g qs))+ where+ myView = divView >>> (productView *** productView)+ powInt = powerView >>> second integerView+ f a = fromMaybe (a, 1) (match powInt a)+ g = build powInt+ rec ((_, 0):xs) ys = rec xs ys+ rec (pair@(a, n):xs) ys =+ case break ((==a) . fst) ys of+ (ys1, (b, m):ys2)+ | m == 0 ->+ rec (pair:xs) (ys1++ys2)+ | otherwise ->+ let i = n `min` m + (ps,qs,rs) = rec ((a, n-i):xs) (ys1++(b,m-i):ys2)+ in (ps, qs, (a,i):rs)+ _ -> + let (ps,qs,rs) = rec xs ys + in (pair:ps, qs,rs)+ rec xs ys = (xs, ys, [])++fractionScale :: Rule Expr+fractionScale = liftRule myView $ + makeSimpleRule (ratId, "rational-scale") $ \((a, e1), (b, e2)) -> do+ guard (e1 /= e2)+ let e3 = lcmExpr e1 e2+ ma <- divisionExpr e3 e1+ mb <- divisionExpr e3 e2+ guard (ma /= 1 || mb /= 1)+ return ((ma*a, e3), (mb*b, e3))+ where+ myView = plusView >>> (divView *** divView)+ +turnIntoFraction :: Rule Expr+turnIntoFraction = liftRule plusView $+ makeSimpleRule (ratId, "to-rational") $ \(a, b) ->+ liftM (\c -> (c, b)) (f a b) `mplus` + liftM (\c -> (a, c)) (f b a)+ where+ f a b = do+ guard (not (a `belongsTo` divView))+ (_, e) <- match divView b+ return $ build divView (a*e, e)++-- A simple implementation that considers the condition stored in the context+checkSolution :: Rule (Context (OrList (Equation Expr)))+checkSolution = makeSimpleRule (ratId, "check-solution") $ + withCM $ oneDisjunct $ \(x :==: a) -> do+ c <- lookupClipboardG "condition"+ xs <- matchM andView c+ guard ((x ./=. a) `elem` xs)+ return false++---------------------------------------------------------------+-- Helper-code+ +condition :: Logic (Relation Expr) -> ContextMonad ()+condition c = do+ mp <- maybeOnClipboardG "condition"+ let a = maybe id (.&&.) mp c+ unless (a==T) (addToClipboardG "condition" a)++conditionNotZero :: Expr -> ContextMonad ()+conditionNotZero expr = condition (f xs)+ where+ f = pushNotWith (Logic.Var . notRelation) . nott+ eq = expr :==: 0+ xs = fmap (build equationView . fmap cleanUpExpr) $ + case match higherDegreeEquationsView (return eq) of+ Just ys -> build orListView (coverUpOrs (build higherDegreeEquationsView ys))+ Nothing -> Logic.Var (coverUp eq)
src/Domain/Math/Polynomial/Rules.hs view
@@ -9,92 +9,95 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Domain.Math.Polynomial.Rules where+module Domain.Math.Polynomial.Rules + ( sameConFactor, abcFormula, allPowerFactors, bringAToOne, cancelTerms+ , commonFactorVar, commonFactorVarNew, defPowerNat, distributeDivision+ , distributeTimes, distributionSquare, exposeSameFactor, factorLeftAsSquare+ , factorVariablePower, flipEquation, higherSubst, merge, moveToLeft, mulZero+ , niceFactors, niceFactorsNew, noDivisionConstant, noLinFormula, oneVar+ , parentNotNegCheck, prepareSplitSquare, quadraticRuleOrder, removeDivision+ , ruleApproximate, ruleNormalizeMixedFraction, ruleNormalizeRational+ , sameFactor, simplerLinearFactor, simplerPolynomial, simplerSquareRoot+ , squareBothSides, substBackVar, varToLeft+ ) where -import Common.Apply-import Common.Context-import Common.Rewriting-import Common.Transformation-import Common.Traversable-import Common.Uniplate (universe, uniplate)+import Common.Library hiding (terms, simplify)+import Common.Uniplate (universe, descend) import Common.Utils-import Common.View hiding (simplify) import Control.Monad-import Data.List (nub, (\\), sort, sortBy)+import Data.List import Data.Maybe+import Data.Ord import Data.Ratio import Domain.Math.Approximation (precision) import Domain.Math.Clipboard import Domain.Math.Data.OrList+import Domain.Math.Data.Polynomial import Domain.Math.Data.Relation-import Domain.Math.Equation.CoverUpRules hiding (coverUpPlus)+import Domain.Math.Equation.BalanceRules+import Domain.Math.Equation.CoverUpRules import Domain.Math.Expr import Domain.Math.Numeric.Views import Domain.Math.Polynomial.CleanUp import Domain.Math.Polynomial.Views-import Domain.Math.Power.Views-import Domain.Math.Simplification-import Prelude hiding (repeat, (^), replicate)-import qualified Domain.Math.Data.Polynomial as P-import qualified Domain.Math.SquareRoot.Views as SQ+import Domain.Math.Power.OldViews+import Domain.Math.Simplification hiding (simplifyWith)+import Domain.Math.SquareRoot.Views +import Prelude hiding ( (^) ) ---------------------------------------------------------------- Rule collection+quadraticRuleOrder :: [Id]+quadraticRuleOrder = + [ getId coverUpTimes, getId (coverUpMinusRightWith oneVar)+ , getId (coverUpMinusLeftWith oneVar), getId (coverUpPlusWith oneVar)+ , getId coverUpPower+ , getId commonFactorVar, getId simplerPolynomial+ , getId niceFactors, getId noLinFormula+ , getId cancelTerms, getId sameConFactor, getId distributionSquare+ , getId allPowerFactors+ ] -linearRules :: [Rule (Context (Equation Expr))]-linearRules = map liftToContext $- [ removeDivision, ruleMulti merge, ruleMulti distributeTimesSomewhere- , varToLeft, coverUpNegate, coverUpTimes- ] ++- map ($ oneVar) - [coverUpPlusWith, coverUpMinusLeftWith, coverUpMinusRightWith]+lineq, quadreq, polyeq :: String+lineq = "algebra.equations.linear"+quadreq = "algebra.equations.quadratic"+polyeq = "algebra.equations.polynomial" -quadraticRules :: [Rule (OrList (Equation Expr))]-quadraticRules = -- abcFormula- [ ruleOnce commonFactorVar, ruleOnce noLinFormula, ruleOnce niceFactors- , ruleOnce simplerPoly, mulZero, coverUpPower, squareBothSides- ] ++- map (ruleOnce . ($ oneVar)) - [coverUpPlusWith, coverUpMinusLeftWith, coverUpMinusRightWith] ++- [ ruleOnce coverUpTimes, ruleOnce coverUpNegate, ruleOnce coverUpNumerator- , ruleOnce prepareSplitSquare, ruleOnce factorLeftAsSquare- , ruleOnce2 (ruleSomewhere merge), ruleOnce cancelTerms- , ruleOnce2 distributeTimesSomewhere- , ruleOnce2 (ruleSomewhere distributionSquare), ruleOnce flipEquation - , ruleOnce moveToLeft, ruleMulti2 (ruleSomewhere simplerSquareRoot)- ]- -higherDegreeRules :: [Rule (OrList (Equation Expr))]-higherDegreeRules = - [ allPowerFactors, sameFactor- ] ++ quadraticRules- ------------------------------------------------------------ -- General form rules: ax^2 + bx + c = 0 +quadraticNF :: View Expr (String, (Rational, Rational, Rational))+quadraticNF = polyNormalForm rationalView >>> second quadraticPolyView+ -- ax^2 + bx = 0 commonFactorVar :: Rule (Equation Expr) commonFactorVar = rhsIsZero commonFactorVarNew +-- Maybe to be replaced by more general factorVariablePower?? commonFactorVarNew :: Rule Expr-commonFactorVarNew = makeSimpleRule "common factor var" $ \expr -> do- (x, (a, b, c)) <- match (polyNormalForm rationalView >>> second quadraticPolyView) expr- guard (c == 0 && b /= 0)- -- also search for constant factor- let d = (if a<0 && b<0 then negate else id) (gcdFrac a b)- return (fromRational d .*. Var x .*. (fromRational (a/d) .*. Var x .+. fromRational (b/d)))+commonFactorVarNew = describe "Common factor variable" $ + makeSimpleRule (quadreq, "common-factor") $ \expr -> do+ (x, (a, b, c)) <- match quadraticNF expr+ guard (b /= 0 && c == 0)+ -- also search for constant factor+ let d | a<0 && b<0 = -gcdFrac a b+ | otherwise = gcdFrac a b+ return (fromRational d .*. Var x .*. (fromRational (a/d) .*. Var x .+. fromRational (b/d))) +gcdFrac :: Rational -> Rational -> Rational+gcdFrac r1 r2 = + if denominator r1 == 1 && denominator r2 == 1+ then fromInteger (numerator r1 `gcd` numerator r2)+ else 1 -- ax^2 + c = 0 noLinFormula :: Rule (Equation Expr)-noLinFormula = makeSimpleRule "no linear term b" $ \(lhs :==: rhs) -> do- guard (rhs == 0)- (x, (a, b, c)) <- match (polyNormalForm rationalView >>> second quadraticPolyView) lhs- guard (b == 0 && c /= 0)- return $ - if a>0 then fromRational a .*. (Var x .^. 2) :==: fromRational (-c)- else fromRational (-a) .*. (Var x .^. 2) :==: fromRational c+noLinFormula = describe "No linear term ('b=0')" $ liftRule myView $ + makeSimpleRule (quadreq, "no-lin") $ \((x, (a, b, c)), rhs) -> do+ guard (rhs == 0 && b == 0 && c /= 0)+ return $ if a>0 then ((x, (a, 0, 0)), -c)+ else ((x, (-a, 0, 0)), c)+ where+ myView = constantRight quadraticNF -- search for (X+A)*(X+B) decomposition niceFactors :: Rule (Equation Expr)@@ -102,12 +105,11 @@ -- search for (X+A)*(X+B) decomposition niceFactorsNew :: Rule Expr-niceFactorsNew = makeSimpleRuleList "nice factors" $ \expr -> do+niceFactorsNew = describe "Find a nice decomposition" $ + makeSimpleRuleList (quadreq, "nice-factors") $ \expr -> do let sign t@(x, (a, b, c)) = if a== -1 then (x, (1, -b, -c)) else t - (x, (a, rb, rc)) <- liftM sign (matchM (polyNormalForm rationalView >>> second quadraticPolyView) expr)+ (x, (a, b, c)) <- liftM sign (matchM (polyNormalForm integerView >>> second quadraticPolyView) expr) guard (a==1)- b <- isInt rb- c <- isInt rc let ok (i, j) = i+j == b f (i, j) | i == j = -- special case@@ -115,67 +117,62 @@ | otherwise = (Var x + fromInteger i) * (Var x + fromInteger j) map f (filter ok (factors c))--rhsIsZero :: Rule Expr -> Rule (Equation Expr)-rhsIsZero r = makeSimpleRuleList (name r) $ \(lhs :==: rhs) -> do- guard (rhs == 0)- a <- applyAll r lhs- return (a :==: rhs)+ where+ factors :: Integer -> [(Integer, Integer)]+ factors n = [ pair+ | let h = (floor :: Double -> Integer) (sqrt (abs (fromIntegral n)))+ , a <- [1..h], let b = n `div` a, a*b == n + , pair <- [(a, b), (negate a, negate b)] + ] -- Simplify polynomial by multiplying (or dividing) the terms: -- 1) If a,b,c are ints, then find gcd -- 2) If any of a,b,c is a fraction, find lcm of denominators -- 3) If a<0, then also suggest to change sign (return two solutions)-simplerPoly :: Rule (Equation Expr)-simplerPoly = makeSimpleRuleList "simpler polynomial" $ \(lhs :==: rhs) -> do- guard (rhs == 0)- let thisView = polyNormalForm rationalView >>> second quadraticPolyView- (x, (a, b, c)) <- matchM thisView lhs- r <- findFactor [a, b, c]- d <- if a >= 0 then [r] else [-r, r]- guard (d `notElem` [0, 1])- return (build thisView (x, (a*d, b*d, c*d)) :==: 0)+simplerPolynomial :: Rule (Equation Expr)+simplerPolynomial = describe "simpler polynomial" $+ rhsIsZero $ liftRuleIn thisView $ + makeSimpleRuleList (quadreq, "simpler-poly") $ \(a, b, c) -> do+ r <- findFactor (filter (/=0) [a, b, c])+ d <- if a >= 0 then [r] else [-r, r]+ guard (d `notElem` [0, 1])+ return (a*d, b*d, c*d) where- findFactor :: Monad m => [Rational] -> m Rational- findFactor rs- | null rs || any (==0) rs = - fail "no factor"- | all ((==1) . denominator) rs = - return $ Prelude.recip $ fromIntegral $ foldr1 gcd $ map numerator rs- | otherwise = - return $ fromIntegral $ foldr1 lcm $ map denominator rs-+ thisView = polyNormalForm rationalView >>> swapView >>> first quadraticPolyView+ -- Simplified variant of simplerPoly: just bring a to 1. -- Needed for quadratic strategy without square formula bringAToOne :: Rule (Equation Expr)-bringAToOne = makeSimpleRule "bring a to one" $ \(lhs :==: rhs) -> do- guard (rhs == 0)- let thisView = polyNormalForm rationalView >>> second quadraticPolyView- (x, (a, b, c)) <- matchM thisView lhs+bringAToOne = rhsIsZero $ liftRuleIn thisView $ + describe "Bring 'a' to one" $ + makeSimpleRule (quadreq, "scale") $ \(a, b, c) -> do guard (a `notElem` [0, 1])- return (build thisView (x, (1, b/a, c/a)) :==: 0)+ return (1, b/a, c/a)+ where+ thisView = polyNormalForm rationalView >>> swapView >>> first quadraticPolyView ------------------------------------------------------------ -- General form rules: expr = 0 -- Rule must be symmetric in side of equation mulZero :: Rule (OrList (Equation Expr))-mulZero = makeSimpleRuleList "multiplication is zero" $ onceJoinM bothSides+mulZero = describe "multiplication is zero" $ + makeSimpleRuleList (quadreq, "product-zero") $ oneDisjunct bothSides where bothSides eq = oneSide eq `mplus` oneSide (flipSides eq) oneSide (lhs :==: rhs) = do guard (rhs == 0) (_, xs) <- matchM productView lhs guard (length xs > 1)- let f e = case match (polyNormalForm rationalView >>> second linearPolyView) e of- -- special cases (simplify immediately, as in G&R)- Just (x, (a, b)) - | a == 1 -> - Var x :==: fromRational (-b)- | a == -1 -> - Var x :==: fromRational b- _ -> e :==: 0 - return $ orList $ map f xs + return $ orList $ flip map xs $ \e ->+ case match (polyNormalForm rationalView >>> second linearPolyView) e of+ -- special cases (simplify immediately, as in G&R)+ Just (x, (a, b)) + | a == 1 -> + Var x :==: fromRational (-b)+ | a == -1 -> + Var x :==: fromRational b+ _ -> e :==: 0 ------------------------------------------------------------ -- Constant form rules: expr = constant@@ -184,39 +181,40 @@ -- Prevent (x^2+3x)+5 = 0 to be covered up oneVar :: ConfigCoverUp oneVar = configCoverUp- { configName = Just "one var"+ { configName = "onevar" , predicateCovered = \a -> p1 a || p2 a- , predicateCombined = noVars+ , predicateCombined = hasNoVar , coverLHS = True , coverRHS = True } where - p1 = (==1) . length . collectVars+ p1 = (==1) . length . vars -- predicate p2 tests for cases such as 12*(x^2-3*x)+8 == 56 p2 a = fromMaybe False $ do (x, y) <- match timesView a- return (hasVars x /= hasVars y)+ return (hasSomeVar x /= hasSomeVar y) ------------------------------------------------------------ -- Top form rules: expr1 = expr2 -- Do not simplify (5+sqrt 53)/2 simplerSquareRoot :: Rule Expr-simplerSquareRoot = makeSimpleRule "simpler square root" $ \e -> do- xs <- f e- guard (not (null xs))- new <- canonical (SQ.squareRootViewWith rationalView) e- ys <- f new- guard (xs /= ys)- return new+simplerSquareRoot = describe "simpler square root" $ + makeSimpleRule (quadreq, "simpler-sqrt") $ \e -> do+ xs <- f e+ guard (not (null xs))+ new <- canonical (squareRootViewWith rationalView) e+ ys <- f new+ guard (xs /= ys)+ return new where -- return numbers under sqrt symbol f :: Expr -> Maybe [Rational]- f e = liftM sort $ sequence [ match rationalView e | Sqrt e <- universe e ]- + f e = liftM sort $ sequence [ match rationalView a | Sqrt a <- universe e ] cancelTerms :: Rule (Equation Expr)-cancelTerms = makeSimpleRule "cancel terms" $ \(lhs :==: rhs) -> do+cancelTerms = describe "Cancel terms" $ + makeSimpleRule (quadreq, "cancel") $ \(lhs :==: rhs) -> do xs <- match sumView lhs ys <- match sumView rhs let zs = filter (`elem` ys) (nub xs)@@ -226,148 +224,146 @@ -- Two out of three "merkwaardige producten" distributionSquare :: Rule Expr-distributionSquare = makeSimpleRule "distribution square" f- where- f (Sym s [a :+: b, Nat 2]) | s == powerSymbol =- return ((a .^. 2) .+. (2 .*. a .*. b) + (b .^. 2))- f (Sym s [a :-: b, Nat 2]) | s == powerSymbol =- return ((a .^. 2) .-. (2 .*. a .*. b) + (b .^. 2))- f _ = Nothing+distributionSquare = describe "distribution square" $+ ruleList (quadreq, "distr-square")+ [ \a b -> (a+b)^2 :~> a^2 + 2*a*b + b^2+ , \a b -> (a-b)^2 :~> a^2 - 2*a*b + b^2+ ] -- a^2 == b^2 squareBothSides :: Rule (OrList (Equation Expr))-squareBothSides = makeSimpleRule "square both sides" $ onceJoinM f - where- f (Sym s1 [a, Nat 2] :==: Sym s2 [b, Nat 2]) | all (==powerSymbol) [s1, s2] = - return $ orList [a :==: b, a :==: -b]- f _ = Nothing+squareBothSides = describe "square both sides" $ + rule (quadreq, "square-both") $ \a b -> + orList [a^2 :==: b^2] :~> orList [a :==: b, a :==: -b] -- prepare splitting a square; turn lhs into x^2+bx+c such that (b/2)^2 is c prepareSplitSquare :: Rule (Equation Expr)-prepareSplitSquare = makeSimpleRule "prepare split square" $ \(lhs :==: rhs) -> do- d <- match rationalView rhs- let myView = polyNormalForm rationalView >>> second quadraticPolyView- (x, (a, b, c)) <- match myView lhs- let newC = (b/2)*(b/2)- newRHS = d + newC - c- guard (a==1 && b/=0 && c /= newC)- return (build myView (x, (a, b, newC)) :==: build rationalView newRHS)+prepareSplitSquare = describe "prepare split square" $ + liftRule myView $+ makeSimpleRule (quadreq, "prepare-split") $ \((x, (a, b, c)), r) -> do+ let newC = (b/2)*(b/2)+ newRHS = r + newC - c+ guard (a==1 && b/=0 && c /= newC)+ return ((x, (a, b, newC)), newRHS)+ where+ myView = constantRight quadraticNF -- factor left-hand side into (ax + c)^2 factorLeftAsSquare :: Rule (Equation Expr)-factorLeftAsSquare = makeSimpleRule "factor left as square" $ \(lhs :==: rhs) -> do- guard (noVars rhs)- (x, (a, b, c)) <- match (polyNormalForm rationalView >>> second quadraticPolyView) lhs- let h = b/2- guard (a==1 && b/=0 && h*h == c)- return ((Var x + build rationalView h)^2 :==: rhs) +factorLeftAsSquare = describe "factor left as square" $+ makeSimpleRule (quadreq, "left-square") $ \(lhs :==: rhs) -> do+ guard (hasNoVar rhs)+ (x, (a, b, c)) <- match quadraticNF lhs+ let h = b/2+ guard (a==1 && b/=0 && h*h == c)+ return ((Var x + build rationalView h)^2 :==: rhs) -- flip the two sides of an equation flipEquation :: Rule (Equation Expr) flipEquation = doBeforeTrans condition $- rule "flip equation" $ \a b ->+ describe "flip equation" $+ rule (lineq, "flip") $ \a b -> (a :==: b) :~> (b :==: a) where condition = makeTrans $ \eq@(lhs :==: rhs) -> do- guard (hasVars rhs && noVars lhs)+ guard (hasSomeVar rhs && hasNoVar lhs) return eq -- Afterwards, merge and sort moveToLeft :: Rule (Equation Expr)-moveToLeft = makeSimpleRule "move to left" $ \(lhs :==: rhs) -> do- guard (rhs /= 0)- let complex = case fmap (filter hasVars) $ match sumView (applyD merge lhs) of- Just xs | length xs >= 2 -> True- _ -> False- guard (hasVars lhs && (hasVars rhs || complex))- let new = applyD mergeT $ applyD sortT $ lhs - rhs- return (new :==: 0)+moveToLeft = describe "Move to left" $+ makeSimpleRule (quadreq, "move-left") $ \(lhs :==: rhs) -> do+ guard (rhs /= 0 && hasSomeVar lhs && (hasSomeVar rhs || isComplex lhs))+ return (collectLikeTerms (sorted (lhs - rhs)) :==: 0)+ where+ isComplex = maybe False ((>= 2) . length . filter hasSomeVar) + . match sumView . applyD merge+ + -- high exponents first, non power-factor terms at the end+ sorted = simplifyWith (sortBy (comparing toPF)) sumView+ toPF = fmap (negate . thd3) . match powerFactorView ruleApproximate :: Rule (Relation Expr)-ruleApproximate = makeSimpleRule "approximate" $ \relation -> do- lhs :==: rhs <- match equationView relation- guard (not (simplify rhs `belongsTo` rationalView))- x <- getVariable lhs- d <- match doubleView rhs- let new = fromDouble (precision 4 d)- return (Var x .~=. new)+ruleApproximate = describe "Approximate irrational number" $+ makeSimpleRule (quadreq, "approx") $ \relation -> do+ lhs :==: rhs <- match equationView relation+ guard (not (simplify rhs `belongsTo` rationalView))+ x <- getVariable lhs+ d <- match doubleView rhs+ let new = fromDouble (precision 4 d)+ return (Var x .~=. new) ruleNormalizeRational :: Rule Expr-ruleNormalizeRational = - ruleFromView "normalize rational number" rationalView+ruleNormalizeRational =+ describe "normalize rational number" $ + ruleFromView (lineq, "norm-rational") rationalView ruleNormalizeMixedFraction :: Rule Expr ruleNormalizeMixedFraction = - ruleFromView "normalize mixed fraction" mixedFractionView--ruleFromView :: Eq a => String -> View a b -> Rule a-ruleFromView s v = makeSimpleRuleList s $ \a -> do- b <- canonicalM v a- guard (a /= b)- return b----------------------------------------------------------------- Helpers and Rest--factors :: Integer -> [(Integer, Integer)]-factors n = concat [ [(a, b), (negate a, negate b)] | a <- [1..h], let b = n `div` a, a*b == n ]- where h = floor (sqrt (abs (fromIntegral n)))--isInt :: MonadPlus m => Rational -> m Integer-isInt r = do- guard (denominator r == 1)- return (numerator r)--gcdFrac :: Rational -> Rational -> Rational-gcdFrac r1 r2 = fromMaybe 1 $ do - a <- isInt r1- b <- isInt r2- return (fromInteger (gcd a b))+ describe "normalize mixed fraction" $+ ruleFromView (lineq, "norm-mixed") mixedFractionView ----------------------------------------------------------- -------- Rules From HDE -- X*A + X*B = X*C + X*D+-- New implementation, but slightly different than original+-- This one does not factor constants+ allPowerFactors :: Rule (OrList (Equation Expr))-allPowerFactors = makeSimpleRule "all power factors" $ onceJoinM $ \(lhs :==: rhs) -> do- xs <- match (sumView >>> listView powerFactorView) lhs- ys <- match (sumView >>> listView powerFactorView) rhs- case unzip3 (filter ((/=0) . snd3) (xs ++ ys)) of- (s:ss, _, ns) | all (==s) ss -> do- let m = minimum ns - make = build (sumView >>> listView powerFactorView) . map f- f (s, i, n) = (s, i, n-m)- guard (m > 0 && length ns > 1)- return $ orList [Var s :==: 0, make xs :==: make ys]- _ -> Nothing+allPowerFactors = describe "all power factors" $+ makeSimpleRule (polyeq, "power-factors") $ oneDisjunct $ + \(lhs :==: rhs) -> do+ let myView = polyNormalForm rationalView+ (s1, p1) <- match myView lhs+ (s2, p2) <- match myView rhs+ let n | p1 == 0 = lowestDegree p2+ | p2 == 0 = lowestDegree p1 + | otherwise = lowestDegree p1 `min` lowestDegree p2+ ts = terms p1 ++ terms p2+ f p = build myView (s1, raise (-n) p)+ guard ((s1==s2 || p1==0 || p2==0) && n > 0 && length ts > 1)+ return $ orList [Var s1 :==: 0, f p1 :==: f p2] +factorVariablePower :: Rule Expr+factorVariablePower = describe "factor variable power" $ + makeSimpleRule (polyeq, "factor-varpower") $ \expr -> do+ let myView = polyNormalForm rationalView+ (s, p) <- match (polyNormalForm rationalView) expr+ let n = lowestDegree p+ guard (n > 0 && length (terms p) > 1)+ return $ Var s .^. fromIntegral n * build myView (s, raise (-n) p)+ -- A*B = A*C implies A=0 or B=C sameFactor :: Rule (OrList (Equation Expr))-sameFactor = makeSimpleRule "same factor" $ onceJoinM $ \(lhs :==: rhs) -> do- (b1, xs) <- match productView lhs- (b2, ys) <- match productView rhs- (x, y) <- safeHead [ (x, y) | x <- xs, y <- ys, x==y, hasVars x ] -- equality is too strong?- return $ orList [ x :==: 0, build productView (b1, xs\\[x]) :==: build productView (b2, ys\\[y]) ]+sameFactor = describe "same factor" $ + makeSimpleRule (quadreq, "same-factor") $ oneDisjunct $ \(lhs :==: rhs) -> do+ (b1, xs) <- match productView lhs+ (b2, ys) <- match productView rhs+ (x, y) <- safeHead [ (x, y) | x <- xs, y <- ys, x==y, hasSomeVar x ] -- equality is too strong?+ return $ orList [ x :==: 0, build productView (b1, xs\\[x]) :==: build productView (b2, ys\\[y]) ] -- N*(A+B) = N*C + N*D recognize a constant factor on both sides -- Example: 3(x^2+1/2) = 6+6x sameConFactor :: Rule (Equation Expr)-sameConFactor = makeSimpleRule "same constant factor" $ \(lhs :==: rhs) -> do- xs <- match sumView lhs- ys <- match sumView rhs- ps <- mapM (match productView) (xs ++ ys) - let (bs, zs) = unzip ps- (rs, es) = unzip (map (f 1 []) zs)- f r acc [] = (r, reverse acc)- f r acc (x:xs) = case match rationalView x of- Just r2 -> f (r*r2) acc xs- Nothing -> f r (x:acc) xs- con <- whichCon rs- guard (con /= 1)- let make b r e = build productView (b, (fromRational (r/con):e))- (newLeft, newRight) = splitAt (length xs) (zipWith3 make bs rs es)- return (build sumView newLeft :==: build sumView newRight)+sameConFactor = + describe "same constant factor" $+ liftRule myView $ + makeSimpleRule (quadreq, "same-con-factor") $ \(ps1 :==: ps2) -> do+ let (bs, zs) = unzip (ps1 ++ ps2)+ (rs, es) = unzip (map (f 1 []) zs)+ f r acc [] = (r, reverse acc)+ f r acc (x:xs) = case match rationalView x of+ Just r2 -> f (r*r2) acc xs+ Nothing -> f r (x:acc) xs+ c <- whichCon rs+ guard (c /= 1)+ let make b r e = (b, fromRational (r/c):e)+ (newLeft, newRight) = splitAt (length ps1) (zipWith3 make bs rs es)+ return (newLeft :==: newRight) where+ myView = bothSidesView (sumView >>> listView productView)+ whichCon :: [Rational] -> Maybe Rational whichCon xs | all (\x -> denominator x == 1 && x /= 0) xs =@@ -375,9 +371,10 @@ | otherwise = Nothing abcFormula :: Rule (Context (OrList (Equation Expr)))-abcFormula = makeSimpleRule "abc formula" $ withCM $ onceJoinM $ \(lhs :==: rhs) -> do+abcFormula = describe "quadratic formula (abc formule)" $ + makeSimpleRule (quadreq, "abc") $ withCM $ oneDisjunct $ \(lhs :==: rhs) -> do guard (rhs == 0)- (x, (a, b, c)) <- matchM (polyNormalForm rationalView >>> second quadraticPolyView) lhs+ (x, (a, b, c)) <- matchM quadraticNF lhs addListToClipboard ["a", "b", "c"] (map fromRational [a, b, c]) let discr = b*b - 4 * a * c sqD = sqrt (fromRational discr)@@ -392,7 +389,8 @@ ] higherSubst :: Rule (Context (Equation Expr))-higherSubst = makeSimpleRule "higher subst" $ withCM $ \(lhs :==: rhs) -> do+higherSubst = describe "Substitute variable" $+ makeSimpleRule (polyeq, "subst") $ withCM $ \(lhs :==: rhs) -> do guard (rhs == 0) let myView = polyView >>> second trinomialPolyView (x, ((a, n1), (b, n2), (c, n3))) <- matchM myView lhs@@ -401,26 +399,26 @@ addToClipboard "subst" (toExpr (Var "p" :==: Var x .^. fromIntegral n2)) return (new :==: 0) -substBackVar :: (Crush f, Crush g) => Rule (Context (f (g Expr)))-substBackVar = makeSimpleRule "subst back var" $ withCM $ \a -> do+substBackVar :: Rule (Context Expr)+substBackVar = describe "Substitute back a variable" $ + makeSimpleRule (polyeq, "back-subst") $ withCM $ \a -> do expr <- lookupClipboard "subst" case fromExpr expr of Just (Var p :==: rhs) -> do- guard (p `elem` concatMap collectVars (concatMap crush (crush a)))- return (fmap (fmap (subst p rhs)) a)+ guard (hasVar p a)+ return (subst p rhs a) _ -> fail "no subst in clipboard" where subst a b (Var c) | a==c = b- subst a b expr = build (map (subst a b) cs)- where (cs, build) = uniplate expr+ subst a b expr = descend (subst a b) expr exposeSameFactor :: Rule (Equation Expr)-exposeSameFactor = makeSimpleRuleList "expose same factor" $ \(lhs :==: rhs) -> do - (bx, xs) <- matchM (productView) lhs- (by, ys) <- matchM (productView) rhs- (nx, ny) <- [ (xs, new) | x <- xs, suitable x, new <- exposeList x ys ] ++- [ (new, ys) | y <- ys, suitable y, new <- exposeList y xs ]- return (build productView (bx, nx) :==: build productView (by, ny))+exposeSameFactor = describe "expose same factor" $ + liftRule (bothSidesView productView) $ + makeSimpleRuleList (polyeq, "expose-factor") $ \((bx, xs) :==: (by, ys)) -> do + (nx, ny) <- [ (xs, new) | x <- xs, suitable x, new <- exposeList x ys ] +++ [ (new, ys) | y <- ys, suitable y, new <- exposeList y xs ]+ return ((bx, nx) :==: (by, ny)) where suitable p = fromMaybe False $ do (_, _, b) <- match (linearViewWith rationalView) p@@ -433,32 +431,22 @@ expose a b = do (s1, p1) <- matchM (polyViewWith rationalView) a (s2, p2) <- matchM (polyViewWith rationalView) b- guard (s1==s2)- case P.division p2 p1 of+ guard (s1==s2 && p1/=p2)+ case division p2 p1 of Just p3 -> return $ map (\p -> build (polyViewWith rationalView) (s1,p)) [p1, p3] Nothing -> [] --------------------------------------------------------- -- From LinearEquations ----------------------------------------------------------- Transformations--plusT, minusT :: Functor f => Expr -> Transformation (f Expr)-plusT e = makeTrans $ return . fmap (applyD mergeT . (.+. e))-minusT e = makeTrans $ return . fmap (applyD mergeT . (.-. e))--timesT :: Functor f => Expr -> Transformation (f Expr)-timesT e = makeTrans $ \eq -> do - r <- match rationalView e- guard (r /= 0)- return $ fmap (applyD mergeT . applyD distributionOldT . (e .*.)) eq--divisionT :: Expr -> Transformation (Equation Expr)-divisionT e = makeTrans $ \eq -> do- r <- match rationalView e- guard (r /= 0)- return $ fmap (applyD mergeT . applyD distributionOldT . (./. e)) eq+-- Only used for cleaning up+distributeAll :: Expr -> Expr+distributeAll expr = + case expr of + e1 :*: e2 -> let as = fromMaybe [e1] (match sumView e1)+ bs = fromMaybe [e2] (match sumView e2)+ in build sumView [ a .*. b | a <- as, b <- bs ]+ _ -> expr -- This rule should consider the associativity of multiplication -- Combine bottom-up, for example: 5*(x-5)*(x+5) @@ -482,89 +470,123 @@ rec _ = [] g :: Expr -> Expr -> [Expr]- g a b = do - as <- matchM sumView a- bs <- matchM sumView b+ g e1 e2 = do + as <- matchM sumView e1 + bs <- matchM sumView e2 guard (length as > 1 || length bs > 1) return $ build sumView [ a .*. b | a <- as, b <- bs ] -mergeT :: Transformation Expr-mergeT = makeTrans $ return . collectLikeTerms---- high exponents first, non power-factor terms at the end-sortT :: Transformation Expr-sortT = makeTrans $ \e -> do- xs <- match sumView e- let f = fmap (negate . thd3) . match powerFactorView- ps = sortBy cmp $ zip xs (map f xs)- cmp (_, ma) (_, mb) = compare ma mb- return $ build sumView $ map fst ps- ------------------------------------------------------- -- Rewrite Rules -varToLeft :: Relational f => Rule (f Expr)-varToLeft = makeRule "variable to left" $ flip supply1 minusT $ \eq -> do- (x, a, _) <- match (linearViewWith rationalView) (rightHandSide eq)- guard (a/=0)- return (fromRational a * Var x)+varToLeft :: Rule (Relation Expr)+varToLeft = doAfter (fmap collectLikeTerms) $ + describe "variable to left" $ + makeRule (lineq, "var-left") $ flip supply1 minusT $ \eq -> do+ (x, a, _) <- match (linearViewWith rationalView) (rightHandSide eq)+ guard (a/=0)+ return (fromRational a * Var x) -- factor is always positive due to lcm function-removeDivision :: Relational r => Rule (r Expr)-removeDivision = makeRule "remove division" $ flip supply1 timesT $ \eq -> do- xs <- match sumView (leftHandSide eq)- ys <- match sumView (rightHandSide eq)- -- also consider parts without variables- -- (but at least one participant should have a variable)- zs <- forM (xs ++ ys) $ \a -> do- (_, list) <- match productView a- return [ (hasVars a, e) | e <- list ]- let f (b, e) = do - (_, this) <- match (divView >>> second integerView) e- return (b, this)- case mapMaybe f (concat zs) of- [] -> Nothing- ps -> let (bs, ns) = unzip ps- in if or bs then return (fromInteger (foldr1 lcm ns))- else Nothing---- Bug fix for distribution in -2*(x+1) (duplicate result)--- This should be a temporary fix-distributeTimesSomewhere :: Rule Expr-distributeTimesSomewhere = makeSimpleRuleList (name distributeTimes) $- nub . map cleanUpSimple . applyAll (ruleSomewhere distributeTimes)+removeDivision :: Rule (Relation Expr)+removeDivision = doAfter (fmap (collectLikeTerms . distributeAll)) $+ describe "remove division" $ + makeRule (lineq, "remove-div") $ flip supply1 timesT $ \eq -> do+ xs <- match sumView (leftHandSide eq)+ ys <- match sumView (rightHandSide eq)+ -- also consider parts without variables+ -- (but at least one participant should have a variable)+ zs <- forM (xs ++ ys) $ \a -> do+ (_, list) <- match productView a+ return [ (hasSomeVar a, e) | e <- list ]+ let f (b, e) = do + (_, this) <- match (divView >>> second integerView) e+ return (b, this)+ case mapMaybe f (concat zs) of+ [] -> Nothing+ ps -> let (bs, ns) = unzip ps+ in if or bs then return (fromInteger (foldr1 lcm ns))+ else Nothing distributeTimes :: Rule Expr-distributeTimes = makeSimpleRuleList "distribution multiplication" $ \expr -> do- new <- applyAll distributionT expr- return (applyD mergeT new)+distributeTimes = describe "distribution multiplication" $ + makeSimpleRuleList (lineq, "distr-times") $+ liftM collectLikeTerms . applyAll distributionT distributeDivision :: Rule Expr-distributeDivision = makeSimpleRule "distribution division" $ \expr -> do- (a, b) <- match divView expr- r <- match rationalView b- xs <- match sumView a- guard (length xs > 1)- let ys = map (/fromRational r) xs- return $ build sumView ys+distributeDivision = describe "distribution division" $+ makeSimpleRule (quadreq, "distr-div") $ \expr -> do+ (xs, r) <- match (divView >>> (sumView *** rationalView)) expr+ guard (length xs > 1)+ let ys = map (/fromRational r) xs+ return $ build sumView ys merge :: Rule Expr-merge = makeSimpleRule "merge similar terms" $ \old -> do- new <- apply mergeT old- guard (old /= new)- return new+merge = describe "merge similar terms" $ + makeSimpleRule (lineq, "merge") $ \old -> do+ let new = collectLikeTerms old+ f = maybe 0 length . match sumView+ guard (f old > f new)+ return new++simplerLinearFactor :: Rule Expr+simplerLinearFactor = describe "simpler linear factor" $ + makeSimpleRule (polyeq, "simpler-linfactor") $ \expr -> do+ let myView = polyNormalForm rationalView >>> second linearPolyView+ (x, (a, b)) <- match myView expr+ let d = (if a<0 then negate else id) (gcdFrac a b)+ guard (a /= 0 && b /= 0 && d /= 1 && d /= -1)+ return $ fromRational d * build myView (x, (a/d, b/d)) ---------------------------- Old+ruleFromView :: (IsId n, Eq a) => n -> View a b -> Rule a+ruleFromView s v = makeSimpleRuleList s $ \a -> do+ b <- canonicalM v a+ guard (a /= b)+ return b+ +rhsIsZero :: Rule Expr -> Rule (Equation Expr)+rhsIsZero r = makeSimpleRuleList (showId r) $ \(lhs :==: rhs) -> do+ guard (rhs == 0)+ a <- applyAll r lhs+ return (a :==: rhs)+ +constantRight :: View Expr a -> View (Equation Expr) (a, Rational)+constantRight v = makeView f g+ where+ f (lhs :==: rhs) = liftM2 (,) (match v lhs) (match rationalView rhs)+ g (a, r) = build v a :==: build rationalView r --- Temporary fix: here we don't care about the terms we apply it to. Only--- use for cleaning up-distributionOldT :: Transformation Expr-distributionOldT = makeTrans f +bothSidesView :: View a b -> View (Equation a) (Equation b)+bothSidesView v = makeView f (fmap (build v)) where- f (a :*: b) =- case (match sumView a, match sumView b) of- (Just as, Just bs) | length as > 1 || length bs > 1 -> - return $ build sumView [ a .*. b | a <- as, b <- bs ]- _ -> Nothing+ f (lhs :==: rhs) = liftM2 (:==:) (match v lhs) (match v rhs)++findFactor :: Monad m => [Rational] -> m Rational+findFactor rs+ | null rs = + fail "no factor"+ | all ((==1) . denominator) rs = + return $ Prelude.recip $ fromIntegral $ foldr1 gcd $ map numerator rs+ | otherwise = + return $ fromIntegral $ foldr1 lcm $ map denominator rs+ +parentNotNegCheck :: Rule (Context Expr)+parentNotNegCheck = minorRule $ makeSimpleRule "parent not negate check" $ \c -> + case up c >>= current of+ Just (Negate _) -> Nothing+ _ -> Just c+ +noDivisionConstant :: Rule Expr+noDivisionConstant = makeSimpleRule (lineq, "no-div-con") f+ where+ f (a :/: b) | hasNoVar b && hasSomeVar a = + return ((1/b) * a)+ f _ = Nothing+ +defPowerNat :: Rule Expr+defPowerNat = makeSimpleRule (polyeq, "def-power-nat") f+ where+ f (Sym _ [Var _, _]) = Nothing -- should not work on x^5+ f (Sym s [a, Nat n]) | isPowerSymbol s = + return (build productView (False, replicate (fromInteger n) a)) f _ = Nothing
src/Domain/Math/Polynomial/Strategies.hs view
@@ -10,15 +10,16 @@ -- ----------------------------------------------------------------------------- module Domain.Math.Polynomial.Strategies - ( linearStrategy, linearMixedStrategy, quadraticStrategy- , higherDegreeStrategy, findFactorsStrategy+ ( linearStrategy, linearMixedStrategy, linearStrategyG+ , quadraticStrategy, quadraticStrategyG+ , higherDegreeStrategy, higherDegreeStrategyG+ , findFactorsStrategy, findFactorsStrategyG ) where import Prelude hiding (repeat, replicate, fail)-import Common.Apply import Common.Strategy import Common.Navigator-import Common.Transformation+import Common.Id import Common.Uniplate (transform) import Common.View import Common.Context@@ -29,153 +30,154 @@ import Domain.Math.Data.Relation import Domain.Math.Expr import Domain.Math.Polynomial.CleanUp+import Data.Maybe+import Common.Rewriting ------------------------------------------------------------ -- Linear equations -linearStrategy :: LabeledStrategy (Equation Expr)-linearStrategy = linearStrategyWith False+linearStrategy :: LabeledStrategy (Context (Equation Expr))+linearStrategy = cleanUpStrategy (applyTop (fmap cleanUpSimple)) linearStrategyG -linearMixedStrategy :: LabeledStrategy (Equation Expr)-linearMixedStrategy = linearStrategyWith True+linearMixedStrategy :: LabeledStrategy (Context (Equation Expr))+linearMixedStrategy = + let f = applyTop (fmap (transform (simplify mixedFractionView) . cleanUpSimple))+ cfg = [ (byName ruleNormalizeMixedFraction, Reinsert)+ , (byName ruleNormalizeRational, Remove)+ ] + in cleanUpStrategy f (configureNow (configure cfg linearStrategyG)) -linearStrategyWith :: Bool -> LabeledStrategy (Equation Expr)-linearStrategyWith mixed = cleanUpStrategy (fmap clean) $- label "Linear Equation" - $ label "Phase 1" (repeat (- removeDivision - <|> ruleMulti distributeTimesSomewhere- <|> ruleMulti merge))+linearStrategyG :: IsTerm a => LabeledStrategy (Context a)+linearStrategyG =+ label "Linear Equation" $+ label "Phase 1" (repeat (+ use removeDivision+ <|> multi (showId distributeTimes) (somewhere (useC parentNotNegCheck <*> use distributeTimes))+ <|> multi (showId merge) (once (use merge)))) <*> label "Phase 2" (repeat (- (flipEquation |> varToLeft)- <|> coverups))- <*> try (ruleMulti final)- where- coverups = coverUpPlus id <|> coverUpTimes <|> coverUpNegate- (clean, final) - | mixed = - ( transform (simplify mixedFractionView) . cleanUpSimple- , ruleNormalizeMixedFraction- )- | otherwise = - (cleanUpSimple, ruleNormalizeRational)- --- helper strategy-coverUpPlus :: (Rule (Equation Expr) -> Rule a) -> Strategy a-coverUpPlus f = alternatives $ map (f . ($ oneVar))- [coverUpPlusWith, coverUpMinusLeftWith, coverUpMinusRightWith]-+ (use flipEquation |> use varToLeft)+ <|> use (coverUpPlusWith oneVar) + <|> use (coverUpMinusLeftWith oneVar)+ <|> use (coverUpMinusRightWith oneVar)+ <|> use coverUpTimes + <|> use coverUpNegate+ ))+ <*> repeat (once + ( use ruleNormalizeRational+ <|> remove (use ruleNormalizeMixedFraction)+ ))+ ------------------------------------------------------------ -- Quadratic equations quadraticStrategy :: LabeledStrategy (Context (OrList (Relation Expr)))-quadraticStrategy = cleanUpStrategy (change cleanUpRelation) $ - label "Quadratic Equation Strategy" $ - repeat $ -- Relaxed strategy: even if there are "nice" factors, allow use of square formula- ( fromEquation generalForm- <|> mapRules (liftRule (contextView (switchView equationView))) generalABCForm- )- |> fromEquation zeroForm - |> fromEquation constantForm- |> simplifyForm- |> fromEquation topForm +quadraticStrategy = + cleanUpStrategy (applyTop cleanUpRelations) quadraticStrategyG++quadraticStrategyG :: IsTerm a => LabeledStrategy (Context a)+quadraticStrategyG = + label "Quadratic Equation Strategy" $ repeat $+ -- Relaxed strategy: even if there are "nice" factors, allow use of quadratic formula+ somewhere (generalForm <|> generalABCForm)+ |> somewhere zeroForm + |> somewhere constantForm+ |> simplifyForm+ |> topForm where- fromEquation = mapRules (liftToContext . liftRule (switchView equationView))- - -- ax^2 + bx + c == 0, without square formula+ -- ax^2 + bx + c == 0, without quadratic formula generalForm = label "general form" $ - ruleOnce commonFactorVar - <|> ruleOnce noLinFormula{- or coverup -}- <|> ruleOnce simplerPoly <|> remove (ruleOnce bringAToOne)- <|> ruleOnce niceFactors - <|> coverUpPower -- to deal with special case x^2=0+ use commonFactorVar+ <|> use noLinFormula+ <|> use simplerPolynomial+ <|> remove (use bringAToOne)+ <|> use niceFactors+ <|> use coverUpPower -- to deal with special case x^2=0+ + generalABCForm = label "abc form" $ + useC abcFormula - generalABCForm = label "abc form" abcFormula- zeroForm = label "zero form" $- toStrategy mulZero+ use mulZero -- expr == c- constantForm = label "constant form" $ - -- coverUpPower <|> -- never used, see coverUpPower rule in general form- ruleOnce coverUpTimes <|> coverUpPlus ruleOnce- <|> ruleOnce coverUpNegate <|> ruleOnce coverUpNumerator - <|> squareBothSides <|> ruleOnce factorLeftAsSquare -+ constantForm = label "constant form" $+ use (coverUpPlusWith oneVar)+ <|> use (coverUpMinusLeftWith oneVar)+ <|> use (coverUpMinusRightWith oneVar)+ <|> use coverUpTimes+ <|> use coverUpNegate+ <|> use coverUpNumerator+ <|> use squareBothSides + <|> use factorLeftAsSquare+ -- simplifies square roots, or do an approximation - simplifyForm = (fromEquation $ - label "square root simplification" $ - toStrategy (ruleMulti2 (ruleSomewhere simplerSquareRoot)))- <|> remove (label "approximate result" $ - toStrategy $ liftToContext (ruleMulti ruleApproximate))+ simplifyForm =+ label "square root simplification" (+ multi (showId simplerSquareRoot) (somewhere (use simplerSquareRoot)))+ <|> + remove (label "approximate result" (+ multi (showId ruleApproximate) (somewhere (use ruleApproximate)))) topForm = label "top form" $- ( ruleOnce2 (ruleSomewhere merge) - <|> ruleOnce cancelTerms - <|> sameFactor <|> ruleOnce sameConFactor- <|> ruleMulti2 (ruleSomewhere distributionSquare)- <|> ruleMulti2 distributeTimesSomewhere - <|> ruleMulti2 (ruleSomewhere distributeDivision)- <|> ruleOnce flipEquation)- |> (ruleOnce moveToLeft <|> remove (ruleOnce prepareSplitSquare))- -- to do: find a better location in the strategy for splitting the square- + somewhere (use cancelTerms <|> use sameFactor)+ |> ( somewhere (use sameConFactor)+ <|> multi (showId merge) (somewhere (use merge))+ <|> somewhere (use distributionSquare)+ <|> multi (showId distributeTimes) (somewhere + (useC parentNotNegCheck <*> use distributeTimes))+ <|> multi (showId distributeDivision) (somewhere + (once (use distributeDivision)))+ <|> somewhere (use flipEquation)+ )+ |> somewhere (use moveToLeft <|> remove (use prepareSplitSquare))+ ----------------------------------------------------------- -- Higher degree equations higherDegreeStrategy :: LabeledStrategy (Context (OrList (Relation Expr)))-higherDegreeStrategy =- label "higher degree" $ - higherForm <*> label "quadratic" ({-option (check isQ2 <*> -} quadraticStrategy)- <*> - cleanUpStrategy (change cleanUpRelation) (label "afterwards" (try (substBackVar <*> f (repeat coverUpPower))))- where- higherForm = cleanUpStrategy (change cleanUpRelation) $ - label "higher degree form" $- repeat (f (toStrategy allPowerFactors) |> - (f (alternatives list) <|> liftRule specialV (ruleOrCtxOnce higherSubst))- |> f (toStrategy (ruleOnce moveToLeft)))- list = map toStrategy - [ coverUpPower, ruleOnce coverUpTimes- , mulZero, {-ruleOnce2 powerFactor,-} sameFactor- , ruleOnce exposeSameFactor- ] ++ [coverUpPlus ruleOnce] ++ [toStrategy (ruleOnce sameConFactor)]- f = mapRulesS (liftToContext . liftRule (switchView equationView))- - specialV :: View (Context (OrList (Relation Expr))) (Context (OrList (Equation Expr)))- specialV = contextView (switchView equationView)--{--isQ2 :: Context (OrList (Relation Expr)) -> Bool-isQ2 = maybe False isQ . match (switchView equationView) . fromContext--isQ :: OrList (Equation Expr) -> Bool-isQ = (`belongsTo` quadraticEquationsView)--}+higherDegreeStrategy = + cleanUpStrategy (applyTop cleanUpRelations) higherDegreeStrategyG --- like ruleOnce: TODO, replace!-ruleOrCtxOnce :: Rule (Context a) -> Rule (Context (OrList a))-ruleOrCtxOnce r = makeSimpleRuleList (name r) $ \ctx -> do- let env = getEnvironment ctx- a <- fromContext ctx- case disjunctions a of- Just xs -> f [] env xs- Nothing -> []+higherDegreeStrategyG :: IsTerm a => LabeledStrategy (Context a)+higherDegreeStrategyG = label "higher degree" $ + higherForm + <*> label "quadratic" quadraticStrategyG+ <*> afterSubst where- f _ _ [] = []- f acc env (a:as) = - case applyAll r (newContext env (noNavigator a)) of- [] -> f (a:acc) env as- new -> concatMap (fmapC $ \na -> orList (reverse acc++na:as)) new- fmapC g c = - case fromContext c of- Just a -> [newContext (getEnvironment c) (noNavigator (g a))]- Nothing -> []+ higherForm = label "higher degree form" $ repeat $+ somewhere (use allPowerFactors)+ |> somewhere (+ use coverUpPower+ <|> use mulZero+ <|> use sameFactor+ <|> use coverUpTimes+ <|> use exposeSameFactor+ <|> use (coverUpPlusWith oneVar)+ <|> use (coverUpMinusLeftWith oneVar)+ <|> use (coverUpMinusRightWith oneVar)+ <|> use sameConFactor+ <|> useC higherSubst)+ |> somewhere (use moveToLeft)+ + afterSubst = label "afterwards" $ try $+ useC substBackVar <*> repeat (somewhere (use coverUpPower)) ----------------------------------------------------------- -- Finding factors in an expression -findFactorsStrategy :: LabeledStrategy Expr-findFactorsStrategy = cleanUpStrategy cleanUpSimple $- label "find factors" $- repeat (niceFactorsNew <|> commonFactorVarNew)+findFactorsStrategy :: LabeledStrategy (Context Expr)+findFactorsStrategy = cleanUpStrategy (applyTop cleanUpSimple) $+ label "find factors" $ replicate 10 $ try findFactorsStrategyG+ +findFactorsStrategyG :: IsTerm a => LabeledStrategy (Context a)+findFactorsStrategyG = label "find factor step" $+ somewhereTimes $ + use niceFactorsNew <|> use commonFactorVarNew + <|> use factorVariablePower <|> use simplerLinearFactor++somewhereTimes :: IsStrategy f => f (Context a) -> Strategy (Context a)+somewhereTimes = somewhereWith "SomewhereTimes" $ \c -> + if isTimesC c then [0 .. arity c-1] else []+ +isTimesC :: Context a -> Bool+isTimesC = maybe False (isJust . isTimes :: Term -> Bool) . currentT
src/Domain/Math/Polynomial/Tests.hs view
@@ -9,49 +9,37 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Domain.Math.Polynomial.Tests where+module Domain.Math.Polynomial.Tests (tests) where -import Control.Monad-import Common.Apply-import Common.Exercise-import Common.Context-import Common.Strategy-import Common.Derivation+import Common.TestSuite import Common.View-import Domain.Math.Data.Relation-import Domain.Math.Data.OrList-import Domain.Math.Clipboard-import Domain.Math.Expr-import Domain.Math.Examples.DWO1-import Domain.Math.Examples.DWO2-import Domain.Math.Polynomial.Exercises-import Domain.Math.Polynomial.IneqExercises import Domain.Math.Polynomial.Generators import Domain.Math.Polynomial.Views-import Domain.Math.Numeric.Laws import Domain.Math.Numeric.Views-import Domain.Logic.Formula+import Domain.Math.Numeric.Laws import Test.QuickCheck-import Data.Maybe ------------------------------------------------------------ -- Testing instances -tests :: IO ()+tests :: TestSuite tests = do let v = viewEquivalent (polyViewWith rationalView) testNumLawsWith v "polynomial" (sized polynomialGen) -- see the derivations for the DWO exercise set+{- seeLE n = printDerivation linearExercise $ concat linearEquations !! (n-1) seeQE n = printDerivation quadraticExercise $ orList $ return $ build equationView $ concat quadraticEquations !! (n-1) seeHDE n = printDerivation higherDegreeExercise $ orList $ return $ build equationView $ higherDegreeEquations !! (n-1) -- test strategies with DWO exercise set+{- testLE = concat $ zipWith (f linearExercise) [1..] $ concat linearEquations testQE = concat $ zipWith (f quadraticExercise) [1..] $ map (orList . return . build equationView) $ concat quadraticEquations testHDE = concat $ zipWith (f higherDegreeExercise) [1..] $ map (orList . return . build equationView) higherDegreeEquations-+-}+f :: (Show b, Show a) => Exercise a -> b -> a -> [b] f s n e = map p (g (applyAll (strategy s) (inContext s e))) where g xs | null xs = error $ show n ++ ": " ++ show e | otherwise = xs@@ -84,13 +72,13 @@ goQE = eqTest ineqQuadraticExercise --eqTest :: Exercise a -> IO ()-eqTest ex = do+eqTest ex = forM_ (examples ex) $ \eq -> do let tree = derivationTree (strategy ex) (inContext ex eq) forM_ (derivations tree) $ \d -> do let xs = terms d pp = maybe "??" (prettyPrinter ex) . fromContext- forM ([ (a, b) | a <- xs, b <- xs ]) $ \(a, b) -> do+ forM [ (a, b) | a <- xs, b <- xs ] $ \(a, b) -> if equalityIneq a b -- equivalence ex (fromContext a) (fromContext b) then putChar '.' else error $ unlines ["", pp a, pp b]@@ -113,4 +101,4 @@ -- temporary fix g (p :&&: q) = g p :&&: g q g (p :||: q) = g p :||: g q- g p = p+ g p = p -}
src/Domain/Math/Polynomial/Views.hs view
@@ -10,9 +10,9 @@ -- ----------------------------------------------------------------------------- module Domain.Math.Polynomial.Views- ( polyView, polyViewFor, polyViewWith, polyViewForWith- , quadraticView, quadraticViewFor, quadraticViewWith, quadraticViewForWith- , linearView, linearViewFor, linearViewWith, linearViewForWith+ ( polyView, polyViewWith -- polyViewFor, polyViewForWith+ , quadraticView, quadraticViewWith --, quadraticViewFor quadraticViewForWith+ , linearView, linearViewWith -- linearViewFor linearViewForWith , constantPolyView, linearPolyView, quadraticPolyView, cubicPolyView , monomialPolyView, binomialPolyView, trinomialPolyView , polyNormalForm@@ -22,22 +22,22 @@ import Prelude hiding ((^)) import Control.Monad-import Common.Apply import Common.View-import Common.Traversable-import Common.Uniplate (transform, uniplate, children)+import Common.Classes+import Common.Rewriting+import Common.Uniplate (transform, descend, children) import Common.Utils (distinct) import Domain.Math.Data.Polynomial import Domain.Math.Data.Relation import Domain.Math.Data.OrList import Domain.Math.Expr import Domain.Math.Numeric.Views-import Domain.Math.Equation.CoverUpRules import Domain.Math.Polynomial.CleanUp+import Domain.Math.Equation.CoverUpRules import Data.Maybe import qualified Domain.Math.Data.SquareRoot as SQ import Domain.Math.SquareRoot.Views-import Domain.Math.Power.Views (powerFactorViewForWith)+import Domain.Math.Power.OldViews (powerFactorViewForWith) ------------------------------------------------------------------- -- Polynomial view@@ -45,22 +45,15 @@ polyView :: View Expr (String, Polynomial Expr) polyView = polyViewWith identity -polyViewFor :: String -> View Expr (Polynomial Expr)-polyViewFor v = polyViewForWith v identity- polyViewWith :: Fractional a => View Expr a -> View Expr (String, Polynomial a)-polyViewWith v = makeView f (uncurry g)+polyViewWith v = makeView matchPoly (uncurry buildPoly) where- f expr = do + matchPoly expr = do pv <- selectVar expr- p <- match (polyViewForWith pv v) expr+ p <- matchPolyFor pv expr return (pv, p) - g pv = build (polyViewForWith pv v)- -polyViewForWith :: Fractional a => String -> View Expr a -> View Expr (Polynomial a)-polyViewForWith pv v = makeView f g- where- f expr = ++ matchPolyFor pv expr = case expr of Var s | pv == s -> Just var Nat n -> Just (fromIntegral n)@@ -71,18 +64,22 @@ a :/: b -> do c <- match v b guard (c /= 0)- guard (pv `notElem` collectVars b)+ guard (withoutVar pv b) p <- f a return (fmap (/c) p)- Sym s [a, n] | s == powerSymbol ->+ Sym s [a, n] | isPowerSymbol s -> liftM2 power (f a) (matchNat n) _ -> do - guard (pv `notElem` collectVars expr)+ guard (withoutVar pv expr) liftM con (match v expr)+ where+ f = matchPolyFor pv - g = build sumView . map h . reverse . terms- h (a, n) = build v a .*. (Var pv .^. fromIntegral n)+ buildPoly pv = + let f (a, n) = build v a .*. (Var pv .^. fromIntegral n)+ in build sumView . map f . reverse . terms + matchNat expr = do n <- match integralView expr guard (n >= 0)@@ -94,36 +91,24 @@ quadraticView :: View Expr (String, Expr, Expr, Expr) quadraticView = quadraticViewWith identity -quadraticViewFor :: String -> View Expr (Expr, Expr, Expr)-quadraticViewFor v = quadraticViewForWith v identity- quadraticViewWith :: Fractional a => View Expr a -> View Expr (String, a, a, a) quadraticViewWith v = polyViewWith v >>> second quadraticPolyView >>> makeView f g where f (s, (a, b, c)) = return (s, a, b, c) g (s, a, b, c) = (s, (a, b, c)) -quadraticViewForWith :: Fractional a => String -> View Expr a -> View Expr (a, a, a)-quadraticViewForWith pv v = polyViewForWith pv v >>> quadraticPolyView- ------------------------------------------------------------------- -- Linear view linearView :: View Expr (String, Expr, Expr) linearView = linearViewWith identity -linearViewFor :: String -> View Expr (Expr, Expr)-linearViewFor v = linearViewForWith v identity- linearViewWith :: Fractional a => View Expr a -> View Expr (String, a, a) linearViewWith v = polyViewWith v >>> second linearPolyView >>> makeView f g where f (s, (a, b)) = return (s, a, b) g (s, a, b) = (s, (a, b)) -linearViewForWith :: Fractional a => String -> View Expr a -> View Expr (a, a)-linearViewForWith pv v = polyViewForWith pv v >>> linearPolyView- ------------------------------------------------------------------- -- Views on polynomials (degree) @@ -166,17 +151,31 @@ polynomialList p = map (`coefficient` p) [d, d-1 .. 0] where d = degree p -list1 (a) = [a]-list2 (a, b) = [a, b]-list3 (a, b, c) = [a, b, c]+list1 :: a -> [a]+list1 a = [a]++list2 :: (a, a) -> [a]+list2 (a, b) = [a, b]++list3 :: (a, a, a) -> [a]+list3 (a, b, c) = [a, b, c]++list4 :: (a, a, a, a) -> [a] list4 (a, b, c, d) = [a, b, c, d] -isList1 [a] = Just a-isList1 _ = Nothing-isList2 [a, b] = Just (a, b)-isList2 _ = Nothing-isList3 [a, b, c] = Just (a, b, c)-isList3 _ = Nothing+isList1 :: [a] -> Maybe a+isList1 [a] = Just a+isList1 _ = Nothing++isList2 :: [a] -> Maybe (a, a)+isList2 [a, b] = Just (a, b)+isList2 _ = Nothing++isList3 :: [a] -> Maybe (a, a, a)+isList3 [a, b, c] = Just (a, b, c)+isList3 _ = Nothing++isList4 :: [a] -> Maybe (a, a, a, a) isList4 [a, b, c, d] = Just (a, b, c, d) isList4 _ = Nothing @@ -248,85 +247,77 @@ in build productView (False, map make xs) :==: 0 higherDegreeEquationsView :: View (OrList (Equation Expr)) (OrList Expr)-higherDegreeEquationsView = makeView f (fmap g)+higherDegreeEquationsView = makeView f (fmap (:==: 0)) where- f = let make (a :==: b) = orList (filter (not . hasNegSqrt) $ map cleanUpExpr2 (normHDE (a-b)))- in Just . normalize . join . fmap make . cuRules+ f = Just . normalize . join . fmap make . coverUpOrs+ make = orList . filter (not . hasNegSqrt) + . map (cleanUpExpr . distr) . normHDE . sub+ sub (a :==: b) = a-b - g = (:==: 0)- - cuRules :: OrList (Equation Expr) -> OrList (Equation Expr)- cuRules xs = - let new = fmap (fmap (cleanUpExpr2 . distr)) xs in- case msum (map (`apply` new) coverUpRulesOr) of- Just ys -> cuRules ys- Nothing -> new+ distr = transform g+ where+ g ((a :+: b) :/: c) = (a ./. c) .+. (b ./. c)+ g ((a :-: b) :/: c) = (a ./. c) .-. (b ./. c)+ g a = a hasNegSqrt :: Expr -> Bool hasNegSqrt (Sqrt a) = case match rationalView a of Just r | r < 0 -> True _ -> hasNegSqrt a-hasNegSqrt (Sym s [a, b]) | s == rootSymbol = +hasNegSqrt (Sym s [a, b]) | isRootSymbol s = case (match rationalView a, match integerView b) of (Just r, Just n) | r < 0 && even n -> True _ -> hasNegSqrt a || hasNegSqrt b hasNegSqrt a = any hasNegSqrt (children a) -distr :: Expr -> Expr-distr = transform f- where- f ((a :+: b) :/: c) = (a ./. c) .+. (b ./. c)- f ((a :-: b) :/: c) = (a ./. c) .-. (b ./. c)- f a = a- normHDE :: Expr -> [Expr] normHDE e = case match (polyViewWith rationalView) e of- Just (x, p) -> g x p+ Just (x, p) -> normPolynomial x p Nothing -> fromMaybe [e] $ do (x, a) <- match (linearEquationViewWith (squareRootViewWith rationalView)) (e :==: 0) return [ Var x .+. build (squareRootViewWith rationalView) (-a) ] - where- g :: String -> Polynomial Rational -> [Expr]- g x p - | d==0 = []- | length (terms p) <= 1 = [Var x]- | d==1 = [Var x .+. fromRational (coefficient 0 p / coefficient 1 p)]- | d==2 = let [a,b,c] = [ coefficient n p | n <- [2,1,0] ]- discr = b*b - 4*a*c- sdiscr = SQ.sqrtRational discr- in if discr < 0 then [] else - map ((Var x .+.) . build (squareRootViewWith rationalView))- [ SQ.scale (1/(2*a)) (SQ.con b + sdiscr)- , SQ.scale (1/(2*a)) (SQ.con b - sdiscr)- ]- | otherwise = - case terms p of - [(c, 0), (b, e1), (a, e2)] | e1 > 1 && e2 `mod` e1 == 0 -> - let list = [(c, 0), (b, 1), (a, e2 `div` e1)]- newp = sum (map (\(x, y) -> scale x (power var y)) list)- sub = map (substitute (x, Var x^fromIntegral e1))- in concatMap normHDE (sub (g x newp))- [(c, 0), (a, n)]- | odd n -> if c/a >= 0 - then [Var x + root (fromRational (c/a)) (fromIntegral n)]- else [Var x - root (fromRational (abs (c/a))) (fromIntegral n)]- | even n -> if c/a > 0- then []- else [ Var x + root (fromRational (abs (c/a))) (fromIntegral n) - , Var x - root (fromRational (abs (c/a))) (fromIntegral n)- ]- _ -> - case factorize p of- ps | length ps > 1 -> concatMap (g x) ps- _ -> [build (polyViewWith rationalView) (x, p)]- where - d = degree p- ++normPolynomial :: String -> Polynomial Rational -> [Expr]+normPolynomial x p + | degree p == 0 = + []+ | length (terms p) <= 1 = + [Var x]+ | degree p == 1 = + [Var x .+. fromRational (coefficient 0 p / coefficient 1 p)]+ | degree p == 2 = + let [a,b,c] = [ coefficient n p | n <- [2,1,0] ]+ discr = b*b - 4*a*c+ sdiscr = SQ.sqrtRational discr+ in if discr < 0 then [] else + map ((Var x .+.) . build (squareRootViewWith rationalView))+ [ SQ.scale (1/(2*a)) (SQ.con b + sdiscr)+ , SQ.scale (1/(2*a)) (SQ.con b - sdiscr)+ ]+ | otherwise = + case terms p of + [(c, 0), (b, e1), (a, e2)] | e1 > 1 && e2 `mod` e1 == 0 -> + let list = [(c, 0), (b, 1), (a, e2 `div` e1)]+ newp = sum (map (\(y, z) -> scale y (power var z)) list)+ sub = map (substitute (x, Var x^fromIntegral e1))+ in concatMap normHDE (sub (normPolynomial x newp))+ [(c, 0), (a, n)]+ | odd n -> if c/a >= 0 + then [Var x + root (fromRational (c/a)) (fromIntegral n)]+ else [Var x - root (fromRational (abs (c/a))) (fromIntegral n)]+ | even n -> if c/a > 0+ then []+ else [ Var x + root (fromRational (abs (c/a))) (fromIntegral n) + , Var x - root (fromRational (abs (c/a))) (fromIntegral n)+ ]+ _ -> + case factorize p of+ ps | length ps > 1 -> concatMap (normPolynomial x) ps+ _ -> [build (polyViewWith rationalView) (x, p)]+ substitute :: (String, Expr) -> Expr -> Expr substitute (s, a) (Var b) | s==b = a-substitute pair expr = f (map (substitute pair) cs)- where - (cs, f) = uniplate expr+substitute pair expr = descend (substitute pair) expr
+ src/Domain/Math/Power/Equation/Exercises.hs view
@@ -0,0 +1,101 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-----------------------------------------------------------------------------+module Domain.Math.Power.Equation.Exercises + ( powerEqExercise+ , expEqExercise+ , logEqExercise+ , higherPowerEqExercise+ ) where++import Prelude hiding ( (^) )++import Common.Context+import Common.Exercise+import Common.View+import Domain.Math.Data.OrList+import Domain.Math.Data.Relation+import Domain.Math.Equation.Views+import Domain.Math.Examples.DWO4+import Domain.Math.Expr hiding (isPower)+import Domain.Math.Numeric.Views+import Domain.Math.Power.Rules+import Domain.Math.Power.Equation.Strategies+import Domain.Math.Power.Equation.NormViews+++------------------------------------------------------------+-- Exercises++powerEqExercise :: Exercise (Relation Expr)+powerEqExercise = let precision = 2 in makeExercise+ { status = Provisional+ , parser = parseExprWith (pRelation pExpr)+ , strategy = powerEqApproxStrategy+ , navigation = termNavigator+ , exerciseId = describe "solve power equation algebraically with x > 0" $ + newId "algebra.manipulation.exponents.equation"+ , examples = concatMap (map $ build equationView) $ + powerEquations ++ [last higherPowerEquations]+ , isReady = solvedRelation+ , isSuitable = (`belongsTo` (normPowerEqApproxView precision))+ , equivalence = viewEquivalent (normPowerEqApproxView precision)+ }+ +expEqExercise :: Exercise (Equation Expr)+expEqExercise = makeExercise+ { status = Provisional+ , parser = parseExprWith (pEquation pExpr)+ , strategy = expEqStrategy+ , navigation = termNavigator+ , exerciseId = describe "solve exponential equation algebraically" $ + newId "algebra.manipulation.exponential.equation"+ , examples = concat expEquations+ , isReady = \ rel -> isVariable (leftHandSide rel) + && rightHandSide rel `belongsTo` rationalView+ , isSuitable = (`belongsTo` normExpEqView)+ , equivalence = viewEquivalent normExpEqView+ , ruleOrdering = ruleOrderingWithId [ getId root2power ] + }++logEqExercise :: Exercise (OrList (Relation Expr))+logEqExercise = makeExercise+ { status = Provisional+ , parser = parseExprWith (pOrList (pRelation pExpr))+ , strategy = logEqStrategy+ , navigation = termNavigator+ , exerciseId = describe "solve logarithmic equation algebraically" $ + newId "algebra.manipulation.logarithmic.equation"+ , examples = map (orList . return . build equationView) (concat logEquations)+ , isReady = solvedRelations+ , isSuitable = (`belongsTo` (switchView equationView >>> normLogEqView))+ , equivalence = viewEquivalent (switchView equationView >>> normLogEqView)+ , ruleOrdering = ruleOrderingWithId [ getId calcPower+ , getId calcRoot ]+ }++higherPowerEqExercise :: Exercise (OrList (Equation Expr))+higherPowerEqExercise = makeExercise+ { status = Provisional+ , parser = parseExprWith (pOrList (pEquation pExpr))+ , strategy = higherPowerEqStrategy+ , navigation = termNavigator+ , exerciseId = describe "solve higher power equation algebraically" $ + newId "algebra.manipulation.exponents.equation"+ , examples = map (orList . return) $ concat $ init higherPowerEquations+ , isReady = solvedRelations+ , isSuitable = maybe False and . disjunctions . fmap (`belongsTo` normPowerEqView)+ , equivalence = let f = normalize . fmap (simplify normPowerEqView')+ in \ x y -> f x == f y+ , ruleOrdering = ruleOrderingWithId [ getId calcPower+ , getId calcRoot ]+ }+
+ src/Domain/Math/Power/Equation/NormViews.hs view
@@ -0,0 +1,215 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-----------------------------------------------------------------------------++module Domain.Math.Power.Equation.NormViews+{- ( normPowerEqApproxView+ , normPowerEqView+ , normExpEqView+ , normLogEqView+-- , normLogView+ ) -} where++import Common.Classes+import Common.View+import Common.Rewriting hiding (rewrite)+import Control.Arrow ( (>>^) )+import Control.Monad+import Data.List+import Data.Maybe+import Data.Ratio+import Domain.Math.Approximation+import Domain.Math.Data.OrList+import Domain.Math.Data.PrimeFactors+import Domain.Math.Data.Relation+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Polynomial.CleanUp+import Domain.Math.Polynomial.Views+import Domain.Math.Power.NormViews+import Domain.Math.Power.Utils+import Domain.Math.Power.Views+import Domain.Math.Simplification hiding (simplify, simplifyWith)++import Common.Uniplate++-- Change to configurable strategy!+normPowerEqApproxView :: Int -> View (Relation Expr) (Expr, Expr)+normPowerEqApproxView d = makeView f (uncurry (.~=.))+ where+ f rel = case relationType rel of + EqualTo -> fmap (second (simplifyWith (precision d) doubleView)) + $ match (equationView >>> normPowerEqView) rel + Approximately -> return (leftHandSide rel, rightHandSide rel)+ _ -> Nothing++normPowerEqView :: View (Equation Expr) (Expr, Expr) -- with x>0!+normPowerEqView = makeView f (uncurry (:==:))+ where+ f expr = do+ -- selected var to the left, the rest to the right+ (lhs :==: rhs) <- varLeft expr >>= constRight+ -- match power+ (c, ax) <- match (timesView <&> (identity >>^ (,) 1)) $+ simplify normPowerView lhs+ (a, x) <- match myPowerView ax+ -- simplify, scale and take root+ let y = cleanUpExpr $ (rhs ./. c) .^. (1 ./. x)+ return (a, simplify rationalView y)++ myPowerView = powerView + <&> (rootView >>> second (makeView (\a->Just (1 ./. a)) (1 ./.)))+ <&> (identity >>^ \a->(a,1))++normPowerEqView' :: View (Equation Expr) (Expr, Expr) -- with x>0!+normPowerEqView' = makeView f (uncurry (:==:))+ where+ f expr = do+ -- selected var to the left, the rest to the right+ (lhs :==: rhs) <- varLeft expr >>= constRight+ -- match power+ (c, (a, x)) <- match unitPowerView lhs+ -- simplify, scale and take root+ let y = cleanUpExpr $ (rhs ./. c) .^. (1 ./. x)+ return (a, simplify myRationalView y)++constRight :: Equation Expr -> Maybe (Equation Expr)+constRight (lhs :==: rhs) = do+ (vs, cs) <- fmap (partition hasSomeVar) (match sumView lhs)+ let rhs' = rhs .+. build sumView (map neg cs)+ return $ negateEq $ build sumView vs :==: simplifyWith mergeAlikeSum sumView rhs'++negateEq :: Equation Expr -> Equation Expr+negateEq (lhs :==: rhs) = + case lhs of+ Negate lhs' -> lhs' :==: neg rhs+ _ -> lhs :==: rhs++varLeft :: Equation Expr -> Maybe (Equation Expr)+varLeft (lhs :==: rhs) = do+ (vs, cs) <- fmap (partition hasSomeVar) (match sumView rhs)+ return $ lhs .+. build sumView (map neg vs) :==: build sumView cs++scaleLeft :: Equation Expr -> Maybe (Equation Expr)+scaleLeft (lhs :==: rhs) = + match timesView lhs >>= \(c, x) -> return $ + x :==: simplifyWith (second mergeAlikeProduct) productView (rhs ./. c)++normExpEqView :: View (Equation Expr) (String, Rational)+normExpEqView = makeView f id >>> linearEquationView+ where+ try g a = fromMaybe a $ g a+ f e = do+ let (l :==: r) = try scaleLeft $ try constRight e+ return $ case match powerView l of+ Just (b, x) -> x :==: simplify normLogView (logBase b r)+ Nothing -> l :==: r++normLogEqView :: View (OrList (Equation Expr)) (OrList (Equation Expr))+normLogEqView = makeView (liftM g . switch . fmap f) id -- AG: needs to be replaced by higherOrderEqView+ where+ f expr@(lhs :==: rhs) = return $+ case match logView lhs of+ Just (b, x) -> x :==: simplify myRationalView (b .^. rhs)+ Nothing -> expr+ g = fmap (fmap (simplify myRationalView) . simplify normPowerEqView) + . simplify quadraticEquationsView ++-- liftToOrListView :: View a b -> View (OrList a) (OrList b)+-- liftToOrListView v = makeView (switch . fmap (match v)) ()++normLogView :: View Expr Expr+normLogView = makeView g id+ where+ g expr = + case expr of + Sym s [x, y] + | isLogSymbol s -> do+ b <- match integerView x+ let divExp (be, n) = return $ f be y ./. fromInteger n+ maybe (Just $ f b y) divExp $ greatestPower b+ | otherwise -> Nothing+ _ -> Nothing+ f b expr= + case expr of+ Nat 1 -> Nat 0+ Nat n + | n == b -> Nat 1+ | otherwise -> maybe (logBase (fromInteger b) (fromInteger n)) Nat + $ lookup b (allPowers n)+ e1 :*: e2 -> f b e1 .+. f b e2+ e1 :/: e2 -> f b e1 .-. f b e2+ Sqrt e -> f b (e .^. (1 ./. 2))+ Negate e -> Negate $ f b e+ Sym s [x,y]+ | isPowerSymbol s -> y .*. f b x+ | isRootSymbol s -> f b (x .^. (1 ./. y))+ _ -> expr++myRationalView :: View Expr Rational+myRationalView = makeView (return . rewrite simplerPower) id >>> rationalView++simplerPower :: Expr -> Maybe Expr+simplerPower expr = + case expr of + Sqrt x -> simplerPower $ x .^. (1/2)+ Sym s [x, y]+ | isRootSymbol s -> simplerPower $ x .^. (1/y)+ | isPowerSymbol s -> f+ | otherwise -> Nothing+ where f | y == 0 || x == 1 = Just 1+ | y == 1 = Just x+ | x == 0 = Just 0+ | otherwise =+ -- geheel getal+ liftM fromRational (match rationalView expr) + `mplus`+ -- wortel+ do + ry <- match rationalView y+ rx <- match rationalView x+ guard $ numerator ry == 1 && denominator rx == 1+ liftM fromInteger $ takeRoot (numerator rx) (denominator ry)+ `mplus`+ -- (a/b)^y -> a^x/b^y+ do+ (a, b) <- match divView x+ return $ build divView (a .^. y, b .^. y)+ _ -> Nothing++-- myRationalView = makeView (exprToNum f) id >>> rationalView+-- where+-- f s [x, y] +-- | isDivideSymbol s = +-- fracDiv x y+-- | isPowerSymbol s = do+-- ry <- match rationalView y+-- rx <- match rationalView x+-- if ry == 0 then return 1 -- 0+-- else if ry == 1 then return rx -- 1+-- else if denominator ry == 1 then -- geheel getal+-- let a = x Prelude.^ abs (numerator ry)+-- in return $ if numerator ry < 0 then 1 / a else a+-- else if numerator ry == 1 then -- breuk / root+-- if denominator ry > 1 then +-- if denominator rx == 1 then+-- takeRoot (numerator rx) (denominator ry) -- breuk/root+-- else+-- f powerSymbol [numerator rx, ] / f powerSymbol []+-- else+-- take+-- else -- no calculation+-- | isRootSymbol s = do+-- n <- match integerView y+-- b <- match integerView x+-- liftM fromInteger $ lookup b $ map swap (allPowers n)+-- f _ _ = Nothing+
+ src/Domain/Math/Power/Equation/Rules.hs view
@@ -0,0 +1,123 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-----------------------------------------------------------------------------++module Domain.Math.Power.Equation.Rules + ( -- * Power equation rules+ commonPower, nthRoot, sameBase, equalsOne, greatestPower+ , approxPower, reciprocalFor+ ) where++import Common.Transformation+import Common.Rewriting+import Common.View hiding (simplify)+import Control.Monad+import Domain.Math.Approximation (precision)+import qualified Domain.Math.Data.PrimeFactors as PF+import Domain.Math.Data.Relation+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Power.Utils+import Domain.Math.Power.Views+import Domain.Math.Simplification (simplify)+++-- | Identifier prefix --------------------------------------------------------++powereq :: String+powereq = "algebra.manipulation.exponents.equation"++-- | Power relation rules -----------------------------------------------------++-- | a^x = b^y => a^(x/c) = b^(y/c) where c = gcd x y+commonPower :: Rule (Equation Expr)+commonPower = makeSimpleRule (powereq, "common-power") $ \expr -> do+ let v = eqView (powerView >>> second integerView)+ ((a, x), (b, y)) <- match v expr+ let c = gcd x y+ guard $ c > 1+ return $ build v ((a, x `div` c), (b, y `div` c))++-- | a^x = n => a^x = b^e+greatestPower :: Rule (Equation Expr)+greatestPower = makeSimpleRule (powereq, "greatest-power") $ \(lhs :==: rhs) -> do+ n <- match integerView rhs+ (_, x) <- match (powerView >>> second integerView) lhs+ (b, e) <- PF.greatestPower n+ guard $ gcd x e > 1+ return $ lhs :==: fromInteger b .^. fromInteger e++-- a^x = c*b^y => a = c*b^(y/x)+nthRoot :: Rule (Equation Expr)+nthRoot = makeSimpleRule (powereq, "nth-root") $ \(lhs :==: rhs) -> do+ guard $ hasSomeVar lhs+ (a, x) <- match powerView lhs+ (c, (b, y)) <- match unitPowerView rhs+ return $ a :==: build unitPowerView (c, (b, simplify (y ./. x)))++-- -- root a x = b => a = b^x+-- nthPower :: Rule (Equation Expr)+-- nthPower = makeSimpleRule (powereq, "nth-power") $ \(lhs :==: rhs) -> do+-- guard $ hasSomeVar lhs+-- (a, x) <- match rootView lhs+-- return $ a :==: rhs .^. x++-- x = a^x => x ~= d+approxPower :: Rule (Relation Expr)+approxPower = makeRule (powereq, "approx-power") $ approxPowerT 2++-- x = a^x => x ~= d+approxPowerT :: Int -> Transformation (Relation Expr)+approxPowerT n = makeTrans $ \ expr ->+ match equationView expr >>= f+ where+ f (Var x :==: d) = + match doubleView d >>= Just . (Var x .~=.) . Number . precision n+ f (d :==: Var x) = + match doubleView d >>= Just . (.~=. Var x) . Number . precision n+ f _ = Nothing++-- -- a*x + c = b*y + d => a*x - b*y = d - c (move vars to the left, cons to the right)+-- varLeftConRight :: Rule (Equation Expr)+-- varLeftConRight = makeSimpleRule (powereq, "var-left-con-right") $ +-- \(lhs :==: rhs) -> do+-- (xs, cs) <- fmap (partition hasSomeVar) (match sumView lhs)+-- (ys, ds) <- fmap (partition hasSomeVar) (match sumView rhs)+-- guard $ length cs > 0 || length ys > 0+-- return $ fmap collectLikeTerms $ +-- build sumView (xs ++ map neg ys) :==: build sumView (ds ++ map neg cs)++-- a^x = a^y => x = y+sameBase :: Rule (Equation Expr)+sameBase = makeSimpleRule (powereq, "same-base") $ \ expr -> do+ ((a, x), (b, y)) <- match (eqView powerView) expr+ guard $ a == b+ return $ x :==: y++-- | c*a^x = d*(1/a)^y => c*a^x = d*a^-y+-- this reciprocal rule is more strict, it demands a same base on the lhs+-- of the equation. Perhaps do this via the enviroment?+reciprocalFor :: Rule (Equation Expr)+reciprocalFor = makeSimpleRule (powereq, "reciprocal-for-base") $ + \ (lhs :==: rhs) -> do+ (_, (a, _)) <- match unitPowerView lhs+ (one, _) <- match divView rhs+ (d, (a'', y)) <- match consPowerView rhs+ guard $ one == 1 && a'' == a+ return $ lhs :==: d .*. a'' .^. negate y++-- | a^x = 1 => x = 0+equalsOne :: Rule (Equation Expr)+equalsOne = makeSimpleRule (powereq, "equals-one") $ \ (lhs :==: rhs) -> do+ guard $ rhs == 1+ (_, x) <- match powerView lhs+ return $ x :==: 0+
+ src/Domain/Math/Power/Equation/Strategies.hs view
@@ -0,0 +1,115 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-----------------------------------------------------------------------------++module Domain.Math.Power.Equation.Strategies+ -- ( powerEqStrategy+ -- , powerEqApproxStrategy+ -- , expEqStrategy+ -- , logEqStrategy+ -- , higherPowerEqStrategy+ -- ) + where++import Prelude hiding (repeat, not)++import Common.Classes+import Common.Context+import Common.Id+import Common.Navigator+import Common.Rewriting+import Common.Strategy+import Common.View (belongsTo)+import Control.Arrow+import Data.Maybe+import Domain.Math.Data.Relation+import Domain.Math.Data.OrList+import Domain.Math.Expr+import Domain.Math.Equation.CoverUpExercise+import Domain.Math.Equation.CoverUpRules+import Domain.Math.Polynomial.CleanUp+import Domain.Math.Polynomial.Strategies (quadraticStrategy, linearStrategy)+import Domain.Math.Polynomial.Rules (flipEquation)+import Domain.Math.Power.Rules+import Domain.Math.Power.Utils+import Domain.Math.Power.Equation.Rules+import Domain.Math.Numeric.Rules++-- | Strategies ---------------------------------------------------------------++powerEqStrategy :: IsTerm a => LabeledStrategy (Context a)+powerEqStrategy = cleanUpStrategy clean strat+ where+ strat = label "Power equation" $ repeat+ $ myCoverUpStrategy+ <*> option (use greatestPower <*> use commonPower)+ <*> use nthRoot+ <*> remove (label "useApprox" $ try $ use approxPower)++ clean = applyD $ exhaustiveUse rules+ rules = onePower : fractionPlus : naturalRules ++ rationalRules++powerEqApproxStrategy :: LabeledStrategy (Context (Relation Expr))+powerEqApproxStrategy = label "Power equation with approximation" $+ configureNow (configure cfg powerEqStrategy)+ where+ cfg = [ (byName (newId "useApprox"), Reinsert) ]++expEqStrategy :: LabeledStrategy (Context (Equation Expr))+expEqStrategy = cleanUpStrategy cleanup strat+ where + strat = label "Exponential equation" + $ myCoverUpStrategy+ <*> repeat (somewhereNotInExp (use factorAsPower))+ <*> repeat (somewhereNotInExp (use reciprocal))+ <*> powerS + <*> (use sameBase <|> use equalsOne)+ <*> linearStrategy+ + cleanup = applyD (exhaustiveUse $ naturalRules ++ rationalRules)+ . applyTop (fmap (mergeConstantsWith isIntRatio))+ + isIntRatio x = x `belongsTo` myIntegerView || x `belongsTo` v+ where v = divView >>> first myIntegerView >>> second myIntegerView+ + powerS = exhaustiveUse [ root2power, addExponents, subExponents+ , mulExponents, simpleAddExponents ]++logEqStrategy :: LabeledStrategy (Context (OrList (Relation Expr)))+logEqStrategy = label "Logarithmic equation"+ $ use logarithm+ <*> try (use flipEquation)+ <*> repeat (somewhere $ use nthRoot + <|> use calcPower + <|> use calcPowerPlus + <|> use calcPowerMinus+ <|> use calcPlainRoot+ <|> use calcPowerRatio)+ <*> quadraticStrategy+++higherPowerEqStrategy :: LabeledStrategy (Context (OrList (Equation Expr)))+higherPowerEqStrategy = cleanUpStrategy cleanup coverUpStrategy+ where + cleanup = applyTop $ fmap $ fmap cleanUpExpr++++-- | Help functions -----------------------------------------------------------++myCoverUpStrategy :: IsTerm a => Strategy (Context a)+myCoverUpStrategy = repeat $ alternatives $ map use coverUpRules++somewhereNotInExp :: IsStrategy f => f (Context a) -> Strategy (Context a)+somewhereNotInExp = somewhereWith "somewhere but not in exponent" f+ where+ f a = if isPowC a then [1] else [0 .. arity a-1]+ isPowC = maybe False (isJust . isPower :: Term -> Bool) . currentT
src/Domain/Math/Power/Exercises.hs view
@@ -9,32 +9,37 @@ -- Portability : portable (depends on ghc) -- -----------------------------------------------------------------------------+ module Domain.Math.Power.Exercises - ( simplifyPowerExercise+ ( -- * Power exercises+ simplifyPowerExercise , powerOfExercise - , nonNegExpExercise+ , nonNegBrokenExpExercise , calcPowerExercise ) where -import Common.Apply +import Prelude hiding ( (^) )++import Common.Classes import Common.Context import Common.Exercise import Common.Navigator+import Common.Rewriting import Common.Strategy hiding (not, replicate) import Common.Utils (distinct) import Common.View import Data.Maybe import Domain.Math.Examples.DWO3-import Domain.Math.Expr+import Domain.Math.Expr hiding (isPower) import Domain.Math.Numeric.Views import Domain.Math.Power.Rules import Domain.Math.Power.Strategies+import Domain.Math.Power.NormViews import Domain.Math.Power.Views-import Prelude hiding ( (^) ) ---------------------------------------------------------------- Exercises +-- | Exercises ----------------------------------------------------------------+ powerExercise :: LabeledStrategy (Context Expr) -> Exercise Expr powerExercise s = makeExercise { status = Provisional@@ -44,82 +49,84 @@ } simplifyPowerExercise :: Exercise Expr-simplifyPowerExercise = (powerExercise powerStrategy)- { description = "simplify expression (powers)"- , exerciseCode = makeCode "math" "simplifyPower"+simplifyPowerExercise = (powerExercise simplifyPowerStrategy)+ { exerciseId = describe "simplify expression (powers)" $ + newId "algebra.manipulation.exponents.simplify" , isReady = isPowerAdd- , isSuitable = (`belongsTo` normPowerView')- , equivalence = viewEquivalent normPowerView'- , examples = concat $ simplerPowers ++ powers1 ++ powers2 + , isSuitable = (`belongsTo` normPowerMapView)+ , equivalence = viewEquivalent normPowerMapView+ , examples = concat $ simplerPowers + ++ powers1 ++ powers2 ++ negExp1 ++ negExp2 ++ normPower1 ++ normPower2 ++ normPower3+ , ruleOrdering = ruleOrderingWithId $ map getId+ [ root2power, subExponents, reciprocalVar, addExponents+ , mulExponents, distributePower ] } powerOfExercise :: Exercise Expr powerOfExercise = (powerExercise powerOfStrategy)- { description = "write as a power of a"- , exerciseCode = makeCode "math" "powerOf"+ { exerciseId = describe "write as a power of a" $ + newId "algebra.manipulation.exponents.powerof" , isReady = isSimplePower , isSuitable = (`belongsTo` normPowerView) , equivalence = viewEquivalent normPowerNonNegRatio- , examples = concat $ powersOfA ++ powersOfX ++ brokenExp1' - ++ brokenExp2 ++ brokenExp3 ++ normPower5'- ++ normPower6+ , examples = concat $ powersOfA ++ powersOfX + ++ brokenExp1' ++ brokenExp2 ++ brokenExp3 + ++ normPower5' ++ normPower6+ , ruleOrdering = ruleOrderingWithId $ map getId+ [ root2power, addExponents, subExponents, mulExponents+ , distributePower, reciprocalVar ] } -nonNegExpExercise :: Exercise Expr-nonNegExpExercise = (powerExercise nonNegExpStrategy)- { description = "write with a non-negative exponent"- , exerciseCode = makeCode "math" "nonNegExp"- , isReady = isPower natView+nonNegBrokenExpExercise :: Exercise Expr+nonNegBrokenExpExercise = (powerExercise nonNegBrokenExpStrategy)+ { exerciseId = describe "write with a non-negative exponent" $ + newId "algebra.manipulation.exponents.nonnegative"+ , isReady = isPower plainNatView , isSuitable = (`belongsTo` normPowerNonNegDouble) , equivalence = viewEquivalent normPowerNonNegDouble , examples = concat $ nonNegExp ++ nonNegExp2 ++ negExp4 ++ negExp5 - ++ brokenExp1 ++ normPower4' ++ normPower5+ ++ brokenExp1 + ++ normPower4' ++ normPower5+ , ruleOrdering = ruleOrderingWithId [ getId mulExponents+ , getId reciprocalFrac+ , getId reciprocalInv+ , getId power2root+ , getId distributePower ] } calcPowerExercise :: Exercise Expr calcPowerExercise = (powerExercise calcPowerStrategy)- { description = "simplify expression (powers)"- , exerciseCode = makeCode "math" "calcPower"+ { exerciseId = describe "simplify expression (powers)" $ + newId "arithmetic.exponents" , isReady = isPowerAdd- , isSuitable = (`belongsTo` normPowerView')- , equivalence = viewEquivalent normPowerView'+ , isSuitable = (`belongsTo` normPowerMapView)+ , equivalence = viewEquivalent normPowerMapView , examples = concat $ negExp3 ++ normPower3' ++ normPower4 } -------------------------------------------------------------------------- Ready checks+-- | Ready checks ------------------------------------------------------------- isSimplePower :: Expr -> Bool-isSimplePower (Sym s [Var _,y]) | s==powerSymbol = y `belongsTo` rationalView+isSimplePower (Sym s [Var _, y]) + | isPowerSymbol s = y `belongsTo` rationalView isSimplePower _ = False isPower :: View Expr a -> Expr -> Bool isPower v expr = - let Just (_, xs) = match productView expr - f (Nat 1 :/: a) = g a- f a = g a- g (Sym s [Var _, a]) | s==powerSymbol = isJust (match v a)- g (Sym s [x, Nat _]) | s==rootSymbol = isPower v x - g (Sqrt x) = g x- g (Var _) = True- g a = a `belongsTo` rationalView- in distinct (concatMap collectVars xs) && all f xs+ let Just (_, xs) = match productView expr + f (Nat 1 :/: a) = g a+ f a = g a+ g (Sym s [Var _, a]) | isPowerSymbol s = isJust (match v a)+ g (Sym s [x, Nat _]) | isRootSymbol s = isPower v x + g (Sqrt x) = g x+ g (Var _) = True+ g a = a `belongsTo` rationalView+ in distinct (concatMap vars xs) && all f xs isPowerAdd :: Expr -> Bool isPowerAdd expr = let Just xs = match sumView expr in all (isPower rationalView) xs && not (applicable calcPowerPlus expr)---- test stuff-{--showDerivations ex = mapM_ (putStrLn . showDerivation ex)--showAllDerivations ex = - mapM_ (\es -> putStrLn (replicate 80 '-') >> showDerivations ex es)- -a = Var "a"-b = Var "b"--}
+ src/Domain/Math/Power/NormViews.hs view
@@ -0,0 +1,147 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-----------------------------------------------------------------------------++module Domain.Math.Power.NormViews + ( -- * Normalising views+ normPowerView, normPowerMapView, normPowerNonNegRatio+ , normPowerNonNegDouble+ ) where++import Prelude hiding ((^), recip)+import qualified Prelude+import Control.Monad+import Common.View+import Data.List+import qualified Data.Map as M+import Domain.Math.Expr+import Domain.Math.Numeric.Views+import Domain.Math.Power.Utils++type PowerMap = (M.Map String Rational, Rational)+++normPowerNonNegRatio :: View Expr (M.Map String Rational, Rational) -- (Rational, M.Map String Rational)+normPowerNonNegRatio = makeView (liftM swap . f) (g . swap)+ where+ f expr = + case expr of+ Sym s [a,b] + | isPowerSymbol s -> do+ (r, m) <- f a+ if r==1 + then do+ r2 <- match rationalView b+ return (1, M.map (*r2) m)+ else do+ n <- match integerView b+ if n >=0 + then return (r Prelude.^ n, M.map (*fromIntegral n) m)+ else return (1/(r Prelude.^ abs n), M.map (*fromIntegral n) m)+ | isRootSymbol s ->+ f (Sym powerSymbol [a, 1/b])+ Sqrt a -> + f (Sym rootSymbol [a,2])+ a :*: b -> do+ (r1, m1) <- f a+ (r2, m2) <- f b+ return (r1*r2, M.unionWith (+) m1 m2)+ a :/: b -> do+ (r1, m1) <- f a+ (r2, m2) <- f b+ guard (r2 /= 0)+ return (r1/r2, M.unionWith (+) m1 (M.map negate m2))+ Var s -> return (1, M.singleton s 1)+ Nat n -> return (toRational n, M.empty)+ Negate x -> do + (r, m) <- f x+ return (negate r, m)+ _ -> do+ r <- match rationalView expr+ return (fromRational r, M.empty)+ g (r, m) = + let xs = [ Var s .^. fromRational a | (s, a) <- M.toList m ]+ in build productView (False, fromRational r : xs)++-- | AG: todo: change double to norm view for rationals+normPowerNonNegDouble :: View Expr (Double, M.Map String Rational)+normPowerNonNegDouble = makeView (liftM (roundof 6) . f) g+ where+ roundof n (x, m) = (fromInteger (round (x * 10.0 ** n)) / 10.0 ** n, m)+ f expr = + case expr of+ Sym s [a,b] + | isPowerSymbol s -> do+ (x, m) <- f a+ y <- match rationalView b+ return (x ** fromRational y, M.map (*y) m)+ | isRootSymbol s -> f (Sym powerSymbol [a, 1/b])+ Sqrt a -> f (Sym rootSymbol [a,2])+ a :*: b -> do+ (r1, m1) <- f a+ (r2, m2) <- f b+ return (r1*r2, M.unionWith (+) m1 m2)+ a :/: b -> do+ (r1, m1) <- f a+ (r2, m2) <- f b+ guard (r2 /= 0)+ return (r1/r2, M.unionWith (+) m1 (M.map negate m2))+ Var s -> return (1, M.singleton s 1)+ Negate x -> do + (r, m) <- f x+ return (negate r, m)+ _ -> do+ d <- match doubleView expr+ return (d, M.empty)+ g (r, m) = + let xs = [ Var s .^. fromRational a | (s, a) <- M.toList m ]+ in build productView (False, fromDouble r : xs)++normPowerMapView :: View Expr [PowerMap]+normPowerMapView = makeView (liftM h . f) g+ where+ f = (mapM (match normPowerNonNegRatio) =<<) . match sumView+ g = build sumView . map (build normPowerNonNegRatio)+ h :: [PowerMap] -> [PowerMap]+ h = map (foldr1 (\(x,y) (_,q) -> (x,y+q))) . groupBy (\x y -> fst x == fst y) . sort++normPowerView :: View Expr (String, Rational)+normPowerView = makeView f g+ where+ f expr = + case expr of+ Sym s [x,y] + | isPowerSymbol s -> do+ (s2, r) <- f x+ r2 <- match rationalView y+ return (s2, r*r2)+ | isRootSymbol s -> + f (x^(1/y))+ Sqrt x ->+ f (Sym rootSymbol [x, 2])+ Var s -> return (s, 1) + x :*: y -> do+ (s1, r1) <- f x+ (s2, r2) <- f y+ guard (s1==s2)+ return (s1, r1+r2)+ Nat 1 :/: y -> do+ (s, r) <- f y+ return (s, -r)+ x :/: y -> do+ (s1, r1) <- f x+ (s2, r2) <- f y+ guard (s1==s2)+ return (s1, r1-r2) + _ -> Nothing+ + g (s, r) = Var s .^. fromRational r+
+ src/Domain/Math/Power/OldViews.hs view
@@ -0,0 +1,55 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-----------------------------------------------------------------------------++module Domain.Math.Power.OldViews where++import Common.Rewriting+import Common.View+import Control.Monad+import Domain.Math.Expr hiding ( (^) )++powerFactorView :: View Expr (String, Expr, Int)+powerFactorView = powerFactorViewWith identity++powerFactorViewWith :: Num a => View Expr a -> View Expr (String, a, Int)+powerFactorViewWith v = makeView f g+ where+ f expr = do+ pv <- selectVar expr+ (e, n) <- match (powerFactorViewForWith pv v) expr+ return (pv, e, n)+ g (pv, e, n) = build (powerFactorViewForWith pv v) (e, n)++powerFactorViewForWith :: Num a => String -> View Expr a -> View Expr (a, Int)+powerFactorViewForWith pv v = makeView f g+ where+ f expr = + case expr of+ Var s | pv == s -> Just (1, 1)+ Negate e -> do+ (a, b) <- f e+ return (negate a, b)+ e1 :*: e2 -> do + (a1, b1) <- f e1+ (a2, b2) <- f e2+ return (a1*a2, b1+b2)+ Sym s [e1, Nat n]+ | isPowerSymbol s -> do + (a1, b1) <- f e1+ a <- match v (build v a1 ^ toInteger n)+ return (a, b1 * fromInteger n)+ _ -> do+ guard (withoutVar pv expr)+ a <- match v expr + return (a, 0)+ + g (a, b) = build v a .*. (Var pv .^. fromIntegral b)
src/Domain/Math/Power/Rules.hs view
@@ -9,46 +9,97 @@ -- Portability : portable (depends on ghc) -- -----------------------------------------------------------------------------+ module Domain.Math.Power.Rules ( -- * Power rules calcPower, calcPowerPlus, calcPowerMinus, addExponents, mulExponents- , subExponents, distributePower, distributePowerDiv, zeroPower, reciprocal- , reciprocalInv+ , subExponents, distributePower, distributePowerDiv, reciprocal+ , reciprocalInv, reciprocalFrac, calcPowerRatio, calcRoot, simplifyPower+ , onePower, powerOne, zeroPower, powerZero, divBase, reciprocalVar+ , reciprocalPower, factorAsPower, calcPlainRoot, simpleAddExponents -- * Root rules- , power2root, root2power, distributeRoot, mulRoot, mulRootCom, divRoot- , simplifyRoot+ , power2root, root2power+ -- * Log rules+ , logarithm -- * Common rules- , myFractionTimes, simplifyFraction, pushNegOut- -- * Help functions- , smartRule+ , myFractionTimes, pushNegOut ) where import Prelude hiding ( (^) ) import qualified Prelude-import Common.Apply++import Common.Classes import Control.Arrow ( (>>^) )+import Common.Id import Common.Transformation import Common.View import Control.Monad import Data.List import Data.Maybe+import qualified Domain.Math.Data.PrimeFactors as PF+import Domain.Math.Data.OrList+import Domain.Math.Data.Relation import Domain.Math.Expr import Domain.Math.Numeric.Views+import Domain.Math.Power.Utils import Domain.Math.Power.Views-import Domain.Math.Polynomial.CleanUp +-- | Identifier prefixes ------------------------------------------------------++power, logarithmic :: String+power = "algebra.manipulation.exponents"+logarithmic = "algebra.manipulation.logarithmic"++ -- | Power rules -------------------------------------------------------------- +-- n => a^e (with e /= 1)+factorAsPower :: Rule Expr+factorAsPower = makeSimpleRuleList (power, "factor-as-power") $ \ expr -> do+ n <- matchM myIntegerView expr+ (a, x) <- PF.allPowers $ toInteger n+ if n > 0+ then return $ fromInteger a .^. fromInteger x+ else if odd x+ then return $ fromInteger (negate a) .^. fromInteger x+ else fail "Could not factorise number."+ calcPower :: Rule Expr -calcPower = makeSimpleRule "calculate power" $ \ expr -> do - (e1, e2) <- match simplePowerView expr- a <- match rationalView e1- x <- match integralView e2- if x > 0 - then return $ fromRational $ a Prelude.^ x- else return $ 1 ./. (e1 .^. neg e2)+calcPower = makeSimpleRule "arithmetic.operation.rational.power" $ \ expr -> do + (a, x) <- match (powerViewWith rationalView plainNatView) expr+ return $ fromRational $ a Prelude.^ x +-- | a^(x/y) => (a^x)^(1/y)+calcPowerRatio :: Rule Expr+calcPowerRatio = makeSimpleRule (power, "power-ratio") $ \ expr -> do+ let v = powerView >>> second (rationalView >>> plainRationalView)+ (a, (x, y)) <- match v expr+ guard $ x /= 1 && y /= 1+ return $ (a .^. fromInteger x) .^. (1 ./. fromInteger y)++-- -- | root n x+calcPlainRoot :: Rule Expr+calcPlainRoot = makeSimpleRule (power, "root") $ \ expr -> do+ (n, x) <- match (rootView >>> (integerView *** integerView)) expr+ y <- takeRoot n x+ return $ fromInteger y++-- | [root n x, ... ]+calcRoot :: Rule (OrList Expr)+calcRoot = makeSimpleRuleList (power, "root") $ \ ors ->+ fromMaybe [] (disjunctions ors) >>= maybeToList . f+ where + f expr = do+ (n, x) <- match (rootView >>> (integerView *** integerView)) expr+ y <- liftM fromInteger $ lookup n $ map swap $ PF.allPowers (abs x)+ let ys | x > 0 && even n = [y, negate y]+ | x > 0 && odd n = [y]+ | x < 0 && odd n = [negate y]+ | otherwise = []+ roots <- toMaybe (not. null) ys+ return $ orList roots+ calcPowerPlus :: Rule Expr calcPowerPlus = makeCommutative sumView (.+.) $ calcBinPowerRule "plus" (.+.) isPlus @@ -57,227 +108,213 @@ calcPowerMinus = makeCommutative sumView (.+.) $ calcBinPowerRule "minus" (.-.) isMinus --- | a*x^y * b*x^q = a*b * x^(y+q)-addExponents :: Rule Expr -addExponents = makeSimpleRuleList "add exponents" $ \ expr -> do- case match (powerFactorisationView unitPowerView) expr of- Just (s, fs) -> do - (e, es) <- split (*) fs- case apply addExponents' e of- Just e' -> return $ build productView (s, e' : es)- Nothing -> fail "" - Nothing -> fail ""+addExponents :: Rule Expr+addExponents = makeSimpleRuleList (power, "add-exponents") $ \ expr -> do+ (sign, fs) <- matchM (powerFactorView isPow) expr+ ((x, y), fill) <- twoNonAdjacentHoles fs+ prod <- applyM addExponentsT $ x * y+ return $ build productView (sign, fill prod) +isPow :: Expr -> Expr -> Bool+isPow x y = x `belongsTo` myIntegerView && + (y `belongsTo` varView || y `belongsTo` powerView) + -- | a*x^y * b*x^q = a*b * x^(y+q)-addExponents' :: Rule Expr -addExponents' = makeSimpleRule "add exponents" $ \ expr -> do- x <- selectVar expr- (e1, e2) <- match timesView expr- (a, y) <- match (unitPowerForView x) e1- (b, q) <- match (unitPowerForView x) e2- return $ build (unitPowerForView x) (a .*. b, y + q)- +addExponentsT :: Transformation Expr +addExponentsT = makeTrans $ \ expr -> do+ (e1, e2) <- match timesView expr+ (a, (x, y)) <- match unitPowerView e1+ (b, (x', q)) <- match unitPowerView e2+ guard $ x == x'+ return $ build unitPowerView (a .*. b, (x, y .+. q))++simpleAddExponents :: Rule Expr+simpleAddExponents = makeRule (power, "simple-add-exponents") addExponentsT+ -- | a*x^y / b*x^q = a/b * x^(y-q) subExponents :: Rule Expr-subExponents = forallVars rule- where- rule x = makeSimpleRule "sub exponents" $ \ expr -> do- (e1, e2) <- match divView expr- (a, y) <- match (unitPowerForView x) e1- (b, q) <- match (unitPowerForView x) e2- return $ build (unitPowerForView x) (a ./. b, y - q)+subExponents = makeSimpleRule (power, "sub-exponents") $ \ expr -> do+ (e1, e2) <- match divView expr+ (a, (x, y)) <- match unitPowerView e1+ (b, (x', q)) <- match unitPowerView e2+ guard $ x == x'+ return $ build unitPowerView (a ./. b, (x, y .-. q)) --- | (c*a^x)^y = c*a^(x*y)+-- | (a^x)^y = a^(x*y) mulExponents :: Rule Expr -mulExponents = makeSimpleRule "mul exponents" $ \ expr -> do- (cax, y) <- match simplePowerView expr- (c, (a, x)) <- match strictPowerView cax- guard (c == 1 || c == -1)- selectVar a- return $ build strictPowerView (c, (a, x .*. y))+mulExponents = makeSimpleRule (power, "mul-exponents") $ \ expr -> do+ ((a, x), y) <- match (strictPowerView >>> first powerView) expr+ return $ build powerView (a, x .*. y) --- | c*(a0..an)^y = c * a0^y * a1^y .. * an^y+-- | (a0 * a1 ... * an)^x = a0^x * a1^x ... * an^x distributePower :: Rule Expr-distributePower = makeSimpleRule "distribute power" $ \ expr -> do- (c, (as', y)) <- match strictPowerView expr- y' <- match integerView y- (sign, as) <- match productView as'- guard (length as > 1)- return $ build productView - (if sign then odd y' else False, c : map (\a -> a .^. y) as)+distributePower = makeSimpleRule (power, "distr-power") $ \ expr -> do+ ((sign, as), x) <- match (powerViewWith productView identity) expr+ guard $ length as > 1+ let y = build productView (False, map (\a -> build powerView (a, x)) as)+ return $ + maybe y (\n -> if odd n && sign then neg y else y) $ match integerView x --- | c * (a/b)^y = c * (a^y / b^y)+-- | (a/b)^y = (a^y / b^y) distributePowerDiv :: Rule Expr-distributePowerDiv = makeSimpleRule "distribute power" $ \ expr -> do- (c, (ab, y)) <- match strictPowerView expr- match integerView y- (a, b) <- match divView ab- return $ c .*. build divView (a .^. y, b .^. y)+distributePowerDiv = makeSimpleRule (power, "distr-power-div") $ \ expr -> do+ ((a, b), y) <- match (powerViewWith divView identity) expr+ return $ build divView (build powerView (a, y), build powerView (b, y)) --- | c*a^0 = c+-- | a^0 = 1 zeroPower :: Rule Expr-zeroPower = makeSimpleRule "zero power" $ \ expr -> do- (_, (c, y)) <- match strictPowerView expr- y' <- match integerView y- guard (y'==0)- return c+zeroPower = makeSimpleRule (power, "power-zero") $ \ expr -> do+ (_, x) <- match powerView expr+ guard $ x == 0+ return 1 --- | d/c*a^x = d*a^(-x)/c-reciprocal :: Rule Expr-reciprocal = makeSimpleRule "reciprocal" $ \ expr -> do- a <- selectVar expr- (d, cax) <- match divView expr- (c, x) <- match (unitPowerForView a) cax- return $ build (unitPowerForView a) (d ./. c, negate x)+-- a ^ 1 = a+onePower :: Rule Expr+onePower = makeSimpleRule (power, "power-one") $ \ expr -> do+ (a, x) <- match powerView expr+ guard $ x == 1+ return a --- | c*a^x = c/a^(-x)-reciprocalInv :: (Expr -> Bool) -> Rule Expr-reciprocalInv p = makeSimpleRule "reciprocal" $ \ expr -> do- guard (p expr)--- a <- selectVar expr- (c, (a, x)) <- match strictPowerView expr- return $ c ./. build strictPowerView (1, (a, neg x))+-- 1 ^ x = 1+powerOne :: Rule Expr+powerOne = makeSimpleRule (power, "one-power") $ \ expr -> do+ (a, _) <- match powerView expr+ guard $ a == 1+ return a +-- 0 ^ x = 0 with x > 0+powerZero :: Rule Expr+powerZero = makeSimpleRule (power, "one-power") $ \ expr -> do+ (a, x) <- match (powerViewWith identity integerView) expr+ guard $ x > 0 && a == 0+ return 0 +-- | all of the above simplification rules+simplifyPower :: Rule Expr+simplifyPower = makeSimpleRuleList (power, "simplify") $ \ expr ->+ mapMaybe (`apply` expr) [zeroPower, onePower, powerOne, powerZero]++-- | e/a = e*a^(-1) where a is an variable+reciprocalVar :: Rule Expr+reciprocalVar = makeSimpleRule (power, "reciprocal-var") $ \ expr -> do+ (e, (c, (a, x))) <- match (divView >>> second unitPowerViewVar) expr+ return $ (e .*. build unitPowerViewVar (1, (a, neg x))) ./. c++-- | c/a^x = c*a^x^(-1)+reciprocalPower :: Rule Expr+reciprocalPower = makeSimpleRule (power, "reciprocal-power") $ \ expr -> do+ (e, (c, (a, x))) <- match (divView >>> second consPowerView) expr+ return $ (e .*. build consPowerView (1, (a, neg x))) ./. c++-- | Use with care, will match any fraction!+reciprocal :: Rule Expr +reciprocal = makeSimpleRule (power, "reciprocal") $+ apply (reciprocalForT identity)++-- | a/b = a*b^(-1)+reciprocalForT :: View Expr a -> Transformation Expr+reciprocalForT v = makeTrans $ \ expr -> do+ (a, b) <- match divView expr+ guard $ b `belongsTo` v+ return $ a .*. build powerView (b, -1)++-- | a^x = 1/a^(-x)+reciprocalInv :: Rule Expr+reciprocalInv = makeSimpleRule (power, "reciprocal-inverse") $ \ expr -> do+ guard $ hasNegExp expr+ (a, x) <- match strictPowerView expr+ return $ 1 ./. build strictPowerView (a, neg x)++-- | c / d*a^(-x)*b^(-y)...p^r... = c*a^x*b^y.../d*p^r...+reciprocalFrac :: Rule Expr+reciprocalFrac = makeSimpleRule (power, "reciprocal-frac") $ \ expr -> do+ (e1, e2) <- match divView expr+ (s, xs) <- match productView e2+ let (ys, zs) = partition hasNegExp xs+ guard (not $ null ys)+ return $ e1 .*. build productView (s, map f ys) ./. build productView (False, zs)+ where+ f e = case match consPowerView e of+ Just (c, (a, x)) -> build consPowerView (c, (a, neg x))+ Nothing -> e++-- | a^x / b^x = (a/b)^x+divBase :: Rule Expr+divBase = describe "divide base of root" $+ makeSimpleRule (power, "divide-base") $ \ expr -> do+ (e1, e2) <- match divView expr+ (c1, (a, x)) <- match consPowerView e1+ (c2, (b, x')) <- match consPowerView e2+ guard $ x == x' && b /= 0+ return $ build consPowerView (c1 .*. c2, (a ./. b, x))++-- | (-a)^x = -(a^x)+pushNegOut :: Rule Expr+pushNegOut = makeSimpleRule (power, "push-negation-out") $ \ expr -> do+ (a, x) <- match (powerViewWith identity integerView) expr+ a' <- isNegate a+ return $ (if odd x then neg else id) $ build powerView (a', fromInteger x)++ -- | Root rules ---------------------------------------------------------------- -- | a^(p/q) = root (a^p) q-power2root :: Rule Expr -power2root = makeSimpleRule "write as root" $ \ expr -> do- (a, pq) <- match simplePowerView expr- (p, q) <- match (rationalView >>> ratioView) pq - guard (q /= 1) - return $ let n = Nat . fromIntegral in root (a .^. n p) $ n q+power2root :: Rule Expr+power2root = makeSimpleRule (power, "write-as-root") $ \ expr -> do+ (a, (p, q)) <- match (strictPowerView >>> second divView) expr+ guard $ q /= 1+ return $ root (a .^. p) q --- | root (a^p) q = a^(p/q)+-- | root a q = a^(1/q) root2power :: Rule Expr -root2power = makeSimpleRule "write as power" $ \ expr -> do- (ap, q) <- match rootView expr- a <- selectVar ap- p <- match (powerViewFor' a) ap- return $ build (powerViewFor' a) (fromRational (p / q))+root2power = makeSimpleRule (power, "write-as-power") $ \ expr -> do+ (a, q) <- match strictRootView expr+ return $ a .^. (1 ./. q) --- | root (a/b) x = root a x / root b x-distributeRoot :: Rule Expr-distributeRoot = makeSimpleRule "distribute root" $ \ expr -> do- (ab, x) <- match rootView expr- (a, b) <- match divView ab- return $ build divView (build rootView (a, x), build rootView (b, x)) --- | c1 root a x * c2 root b x = c1*c2 * root (a*b) x-mulRoot :: Rule Expr-mulRoot = makeSimpleRule "multipy base of root" $ \ expr -> do- (r1, r2) <- match timesView expr- (c1, (a, x)) <- match rootConsView r1- (c2, (b, x')) <- match rootConsView r2- guard (x == x')- return $ build rootConsView (c1 .*. c2, (a .*. b, x))---- | commutative version of the mulRoot rule-mulRootCom :: Rule Expr-mulRootCom = makeCommutative (myProductView (powerFactorisationView rootView)) (.*.) mulRoot- where- myProductView :: View Expr (Bool, [Expr]) -> View Expr [Expr]- myProductView v = v >>> makeView f g- where- f (s, (x:xs)) = return $ if s then neg x : xs else x:xs- f _ = fail ""- g = (,) False ---- | c1 * root a x / c2 * root b x = c1*c2 * root (a/b) x-divRoot :: Rule Expr-divRoot = makeSimpleRule "divide base of root" $ \ expr -> do- (r1, r2) <- match divView expr- (c1, (a, x)) <- match rootConsView r1- (c2, (b, x')) <- match rootConsView r2- guard (x == x' && b /= 0)- return $ build rootConsView (c1 .*. c2, (a ./. b, x))+-- | Logarithmic relation rules ----------------------------------------------- --- | root 0 x = 0 ; root 1 x = 1 ; root a 1 = a-simplifyRoot :: Rule Expr-simplifyRoot = makeSimpleRule "simplify root" $ \e -> f e `mplus` g e- where- f expr = do- (e1, _) <- match rootView expr- x <- match integerView e1- case x of- 0 -> Just 0- 1 -> Just 1- _ -> Nothing- g expr = do- (e1, e2) <- match rootView expr- if e2 == 1 then Just e1 else Nothing+logarithm :: Rule (Equation Expr)+logarithm = makeSimpleRule (logarithmic, "logarithm") $ \(lhs :==: rhs) -> do+ (b, x) <- match logView lhs+ return $ x :==: build powerView (b, rhs) -- | Common rules -------------------------------------------------------------- -- | a/b * c/d = a*c / b*d (b or else d may be one) myFractionTimes :: Rule Expr-myFractionTimes = smartRule $ makeSimpleRule "fraction times" $ \ expr -> do+myFractionTimes = smartRule $ makeSimpleRule (power, "fraction-times") $ \ expr -> do (e1, e2) <- match timesView expr- guard $ isJust $ match divView e1 `mplus` match divView e2+ guard $ e1 `belongsTo` divView || e2 `belongsTo` divView (a, b) <- match (divView <&> (identity >>^ \e -> (e,1))) e1 (c, d) <- match (divView <&> (identity >>^ \e -> (e,1))) e2--- (a, b) <- match divView e1--- (c, d) <- match divView e2 return $ build divView (a .*. c, b .*. d) --- | simplify expression-simplifyFraction :: Rule Expr-simplifyFraction = makeSimpleRule "simplify fraction" $ \ expr -> do- let expr' = simplifyWith (second normalizeProduct) productView $ expr- guard (expr /= expr')- guard $ not $ applicable myFractionTimes expr' -- a hack, need to come up with a constructive solution- return expr' --- | (-a)^x = (-)a^x-pushNegOut :: Rule Expr-pushNegOut = makeSimpleRule "push negation out" $ \ expr -> do- (a, x) <- match simplePowerView expr- a' <- isNegate a- x' <- match integerView x- return $ (if odd x' then neg else id) $ build simplePowerView (a', x)-- -- | Help functions ----------------------------------------------------------- -smartRule :: Rule Expr -> Rule Expr-smartRule = doAfter f- where- f (a :*: b) = a .*. b- f (a :/: b) = a ./. b- f (Negate a) = neg a- f (a :+: b) = a .+. b- f (a :-: b) = a .-. b- f e = e- calcBinPowerRule :: String -> (Expr -> Expr -> Expr) -> (Expr -> Maybe (Expr, Expr)) -> Rule Expr calcBinPowerRule opName op m = - makeSimpleRule ("calculate power " ++ opName) $ \e -> do- (e1, e2) <- m e- (a, (c1, x)) <- match unitPowerView e1- (b, (c2, y)) <- match unitPowerView e2- guard (a == b && x == y)- return (build unitPowerView (a, ((op c1 c2), x)))+ makeSimpleRule (power, "calc-power", opName) $ \e -> do+ (e1, e2) <- m e+ (c1, (a, x)) <- match unitPowerViewVar e1+ (c2, (b, y)) <- match unitPowerViewVar e2+ guard $ a == b && x == y+ return $ build unitPowerViewVar (op c1 c2, (a, x)) +-- use twoNonAdHoles instead of split ??? makeCommutative :: View Expr [Expr] -> (Expr -> Expr -> Expr) -> Rule Expr -> Rule Expr-makeCommutative view op rule = - makeSimpleRuleList (name rule) $ \ expr -> do+makeCommutative view op r = + makeSimpleRuleList (getId r) $ \ expr -> case match view expr of Just factors -> do (e, es) <- split op factors- case apply rule e of+ case apply r e of Just e' -> return $ build view (e' : es)- Nothing -> fail ""- Nothing -> fail ""--split :: (Eq a) => (a -> a -> t) -> [a] -> [(t, [a])] -split op xs = f xs- where- f (y:ys) | not (null ys) = [(y `op` z, xs \\ [y, z]) | z <- ys] ++ f ys - | otherwise = []- f [] = []+ Nothing -> []+ Nothing -> [] -forallVars :: (String -> Rule Expr) -> Rule Expr-forallVars ruleFor = makeSimpleRuleList (name (ruleFor "")) $ \ expr -> - mapMaybe (\v -> apply (ruleFor v) expr) $ collectVars expr+hasNegExp :: Expr -> Bool+hasNegExp expr = fromMaybe False $ + fmap ((< 0) . snd . snd) (match consPowerView expr)
src/Domain/Math/Power/Strategies.hs view
@@ -9,148 +9,77 @@ -- Portability : portable (depends on ghc) -- -----------------------------------------------------------------------------+ module Domain.Math.Power.Strategies- ( powerStrategy+ ( -- * Power strategies+ simplifyPowerStrategy , powerOfStrategy , calcPowerStrategy- , nonNegExpStrategy+ , nonNegBrokenExpStrategy ) where -import Common.Apply+import Prelude hiding (repeat, not)++import Common.Classes import Common.Context+import Common.Id+import Common.Navigator import Common.Strategy import Common.Transformation-import Common.View import Domain.Math.Expr+import Domain.Math.Numeric.Rules (divisionNumerator, divisionDenominator) import Domain.Math.Power.Rules-import Domain.Math.Power.Views-import Domain.Math.Numeric.Rules-import Domain.Math.Numeric.Views-import Prelude hiding (repeat)+import Domain.Math.Power.Utils+import Domain.Math.Simplification ---------------------------------------------------------------- Strategies -powerStrategy :: LabeledStrategy (Context Expr)-powerStrategy = makeStrategy "simplify" rules cleanupRules- where - rules = powerRules - cleanupRules = calcPower : naturalRules ++ rationalRules+-- | Strategies --------------------------------------------------------------- +simplifyPowerStrategy :: LabeledStrategy (Context Expr)+simplifyPowerStrategy = cleanUpStrategyRules "Simplify" powerRules + powerOfStrategy :: LabeledStrategy (Context Expr)-powerOfStrategy = makeStrategy "write as power of" rules cleanupRules- where- rules = powerRules - cleanupRules = calcPower - : simplifyRoot - : simplifyFraction - : naturalRules - ++ rationalRules+powerOfStrategy = cleanUpStrategyRules "Write as power of" powerRules -nonNegExpStrategy :: LabeledStrategy (Context Expr)-nonNegExpStrategy = makeStrategy "non negative exponent" rules cleanupRules+nonNegBrokenExpStrategy :: LabeledStrategy (Context Expr)+nonNegBrokenExpStrategy = cleanUpStrategy (change cleanup . applyTop cleanup) strategy where- rules = [ addExponents- , subExponents- , mulExponents- , reciprocalInv hasNegExp- , distributePower- , distributePowerDiv- , power2root- , distributeRoot- , zeroPower- , calcPowerPlus- , calcPowerMinus- , myFractionTimes- ] ++ fractionRules - cleanupRules = calcPower : simplifyFraction : naturalRules+ rs = [ addExponents, subExponents, mulExponents, reciprocalInv+ , distributePower, distributePowerDiv, power2root, zeroPower+ , calcPowerPlus, calcPowerMinus+ ]+ strategy = label "Write with non-negative exponent" $ exhaustiveStrategy rs+ cleanup = applyD divisionNumerator+ . applyD myFractionTimes+ . mergeConstants + . simplifyWith simplifyConfig {withMergeAlike = False} calcPowerStrategy :: LabeledStrategy (Context Expr)-calcPowerStrategy = makeStrategy "calcPower" rules cleanupRules+calcPowerStrategy = cleanUpStrategy cleanup strategy where- rules = calcPower - : mulRootCom- : divRoot - : rationalRules- cleanupRules = rationalRules ++ naturalRules+ strategy = label "Calculate power" $ exhaustiveStrategy rules+ rules = calcPower : divisionDenominator : reciprocalInv : divBase : rationalRules+ cleanup = applyTop (applyD myFractionTimes)+ . applyD (exhaustiveStrategy $ myFractionTimes : naturalRules) ---------------------------------------------------------------- | Help functions -makeStrategy :: String -> [Rule Expr] -> [Rule Expr] -> LabeledStrategy (Context Expr)-makeStrategy l rs cs = cleanUpStrategy f $ strategise l rs- where- f = applyD $ strategise l cs- strategise l = label l . repeat . alternatives . map (somewhere . liftToContext)+-- | Rule collections --------------------------------------------------------- +powerRules :: [Rule Expr] powerRules =- [ addExponents- , subExponents- , mulExponents- , distributePower- , zeroPower- , reciprocal- , root2power- , distributeRoot- , calcPower- , calcPowerPlus- , calcPowerMinus- , myFractionTimes- , pushNegOut- ]+ [ addExponents, subExponents, mulExponents, distributePower, zeroPower+ , reciprocalVar, root2power, calcPower, calcPowerPlus, calcPowerMinus+ , pushNegOut+ ] -hasNegExp expr = - case match strictPowerView expr of- Just (_, (_, x)) -> case match rationalView x of- Just x' -> x' < 0- _ -> False- _ -> False +-- | Help functions ----------------------------------------------------------- --- | Allowed numeric rules-naturalRules =- [ calcPlusWith "nat" natView- , calcMinusWith "nat" natView- , calcTimesWith "nat" natView- , calcDivisionWith "nat" natView- , doubleNegate- , negateZero- , plusNegateLeft- , plusNegateRight- , minusNegateLeft- , minusNegateRight- , timesNegateLeft- , timesNegateRight - , divisionNegateLeft- , divisionNegateRight - ]- where- natView = makeView f fromInteger- where- f (Nat n) = Just n- f _ = Nothing- -rationalRules = - [ calcPlusWith "rational" rationalRelaxedForm- , calcMinusWith "rational" rationalRelaxedForm- , calcTimesWith "rational" rationalRelaxedForm- , calcDivisionWith "int" integerNormalForm- , doubleNegate- , negateZero- , divisionDenominator- , divisionNumerator- , simplerFraction- ]- -fractionRules =- [ fractionPlus, fractionPlusScale, fractionTimes- , calcPlusWith "int" integerNormalForm- , calcMinusWith "int" integerNormalForm- , calcTimesWith "int" integerNormalForm -- not needed?- , calcDivisionWith "int" integerNormalForm- , doubleNegate- , negateZero- , smartRule divisionDenominator - , smartRule divisionNumerator - , simplerFraction- ]+cleanUpStrategyRules :: IsId n => n -> [Rule Expr] -> LabeledStrategy (Context Expr)+cleanUpStrategyRules l = + cleanUpStrategy (change cleanUp. applyTop cleanUp) . label l . exhaustiveStrategy++cleanUp :: Expr -> Expr+cleanUp = mergeConstants + . simplifyWith simplifyConfig {withMergeAlike = False}+
+ src/Domain/Math/Power/Utils.hs view
@@ -0,0 +1,185 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 : alex.gerdes@ou.nl+-- Stability : provisional+-- Portability : portable (depends on ghc)+--+-- some of these help functions may have a broader scope and could be +-- moved to other parts of the framework (eg. Common)+--+-----------------------------------------------------------------------------++module Domain.Math.Power.Utils where++import Prelude hiding (repeat, replicate)++import Common.Context+import Common.Rewriting+import Common.Strategy hiding (not)+import Common.Transformation+import Common.View+import Control.Monad+import Data.List hiding (repeat, replicate)+import Data.Ratio+import qualified Domain.Math.Data.PrimeFactors as PF+import Domain.Math.Data.Relation+import Domain.Math.Expr+import Domain.Math.Numeric.Rules+import Domain.Math.Numeric.Views+++-- | Strategy functions -------------------------------------------------------++exhaustiveStrategy :: IsTerm a => [Rule a] -> Strategy (Context a)+exhaustiveStrategy = exhaustiveSomewhere . map liftToContext++exhaustiveUse :: (IsTerm a, IsTerm b) => [Rule a] -> Strategy (Context b)+exhaustiveUse = exhaustiveSomewhere . map use++exhaustiveSomewhere :: IsStrategy f => [f (Context a)] -> Strategy (Context a)+exhaustiveSomewhere = repeat . somewhere . alternatives++-- | Rule functions -----------------------------------------------------------++smartRule :: Rule Expr -> Rule Expr+smartRule = doAfter f+ where+ f (a :*: b) = a .*. b+ f (a :/: b) = a ./. b+ f (Negate a) = neg a+ f (a :+: b) = a .+. b+ f (a :-: b) = a .-. b+ f e = e+ +mergeConstantsWith :: (Expr -> Bool) -> Expr -> Expr+mergeConstantsWith p = simplifyWith f productView+ where+ f (sign, xs) = + let (cs, ys) = partition p xs+ c = simplify rationalView $ build productView (False, cs)+ in if maybe False (> 1) (match rationalView c) + then (sign, c:ys) + else (sign, xs)++mergeConstants :: Expr -> Expr+mergeConstants = mergeConstantsWith (`belongsTo` rationalView)++-- | View functions -----------------------------------------------------------++(<&>) :: (MonadPlus m) => ViewM m a b -> ViewM m a b -> ViewM m a b+v <&> w = makeView (\x -> match v x `mplus` match w x) (build v)++infixl 1 <&>++plainNatView :: View Expr Integer+plainNatView = makeView f Nat+ where+ f (Nat n) = Just n+ f _ = Nothing++myIntegerView :: View Expr Integer+myIntegerView = makeView f fromInteger+ where+ f (Nat n) = Just n+ f (Negate (Nat n)) = Just $ negate n+ f _ = Nothing++plainRationalView :: View Rational (Integer, Integer)+plainRationalView = + makeView (\x -> return (numerator x, denominator x)) (uncurry (%))++eqView :: View a b -> View (Equation a) (b, b)+eqView v = eqv >>> v *** v+ where+ eqv = makeView (\(lhs :==: rhs) -> Just (lhs, rhs)) (uncurry (:==:))+++-- | Rule collections ---------------------------------------------------------++naturalRules :: [Rule Expr]+naturalRules =+ [ calcPlusWith "nat" plainNatView, calcMinusWith "nat" plainNatView+ , calcTimesWith "nat" plainNatView, calcDivisionWith "nat" plainNatView+ , doubleNegate, negateZero , plusNegateLeft, plusNegateRight+-- , minusNegateLeft+ , minusNegateRight, timesNegateLeft, timesNegateRight, divisionNegateLeft+ , divisionNegateRight+ ]++rationalRules :: [Rule Expr]+rationalRules = + [ calcPlusWith "rational" rationalRelaxedForm+ , calcMinusWith "rational" rationalRelaxedForm+ , calcTimesWith "rational" rationalRelaxedForm+ , calcDivisionWith "integer" integerNormalForm+ , doubleNegate, negateZero, divisionDenominator, divisionNumerator+ , simplerFraction+ ]+ +fractionRules :: [Rule Expr]+fractionRules =+ [ fractionPlus, fractionPlusScale, fractionTimes+ , calcPlusWith "integer" integerNormalForm+ , calcMinusWith "integer" integerNormalForm+ , calcTimesWith "integer" integerNormalForm -- not needed?+ , calcDivisionWith "integer" integerNormalForm+ , doubleNegate, negateZero, smartRule divisionDenominator+ , smartRule divisionNumerator, simplerFraction+ ]+++-- | Common functions ---------------------------------------------------------++takeRoot :: Integer -> Integer -> Maybe Integer+takeRoot n x = do+ y <- if (abs n == 1) + then Just 1+ else lookup x $ map swap $ PF.allPowers (abs n)+ guard $ n > 0 || (n < 0 && odd x)+ return $ if n > 0 then y else negate y++swap :: (a, b) -> (b, a)+swap (a, b) = (b, a)++split :: (Eq a) => (a -> a -> t) -> [a] -> [(t, [a])] +split op xs = f xs+ where+ f (y:ys) | not (null ys) = [(y `op` z, xs \\ [y, z]) | z <- ys] ++ f ys + | otherwise = []+ f [] = []++toMaybe :: (a -> Bool) -> a -> Maybe a+toMaybe p x = if p x then Just x else Nothing++joinBy :: Eq a => (a -> a -> Bool) -> [a] -> [[a]]+joinBy _ [] = []+joinBy eq xs = ys : joinBy eq (xs \\ ys)+ where+ ys = dropUntil eq xs ++dropUntil :: (a -> a -> Bool) -> [a] -> [a]+dropUntil _ [] = []+dropUntil _ [x] = [x]+dropUntil p (x:y:ys) | p x y = x : dropUntil p (y:ys) + | otherwise = [x]++holes :: [a] -> [(a, [a], a -> [a])]+holes xs = map f [0 .. length xs - 1] + where + f i = let (ys, z:zs) = splitAt i xs + in (z, ys ++ zs, \x -> ys ++ x:zs)++twoNonAdjacentHoles :: [a] -> [((a, a), a -> [a])]+twoNonAdjacentHoles xs = concatMap g pairs+ where+ pairs = [(x, y) | x <- [0 .. length xs - 1], y <- [x + 1 .. length xs - 1]]+ g (x, y) = let (ys, z:zs) = splitAt x xs + (ps, q:qs) = splitAt (y - x - 1) zs + in if null ps+ then [ ((z, q), \a -> ys ++ a:ps ++ qs) ]+ else [ ((z, q), \a -> ys ++ a:ps ++ qs)+ , ((z, q), \a -> ys ++ ps ++ a:qs) ]
src/Domain/Math/Power/Views.hs view
@@ -10,345 +10,151 @@ -- ----------------------------------------------------------------------------- -module Domain.Math.Power.Views +module Domain.Math.Power.Views ( -- * Power views- strictPowerView, strictPowerViewFor, powerConsViewFor, powerConsView- , unitPowerView, unitPowerForView, simplePowerView, powerFactorisationView- , powerFactorViewWith, powerViewFor', powerFactorViewForWith- , powerViewFor, powerFactorView- -- * Root views- , rootView, rootConsView- -- * View combinator- , (<&>)- -- * Normalising views- , normPowerView, normPowerView', normPowerNonNegRatio- , normPowerNonNegDouble+ powerView, powerViewWith, powerViewForWith, powerViewFor, powerFactorView+ , consPowerView, consPowerViewForWith, consPowerViewFor,consPowerViewForVar+ , unitPowerViewForVar, unitPowerViewVar, unitPowerView, strictPowerView+ , rootView, strictRootView+ -- * Log view+ , logView -- * Other views- , ratioView, natView+ , plainNatView, plainRationalView, varView ) where -import Prelude hiding ((^), recip)-import qualified Prelude import Control.Arrow ( (>>^) ) import Control.Monad+import Common.Rewriting import Common.View-import Data.List-import qualified Data.Map as M-import Data.Maybe-import Data.Ratio import Domain.Math.Expr-import Domain.Math.Numeric.Views+import Domain.Math.Power.Utils --- | Combinator function-(<&>) :: (MonadPlus m) => ViewM m a b -> ViewM m a b -> ViewM m a b-v <&> w = makeView f g- where- f x = match v x `mplus` match w x- g = build v-infixl <&>+-- | Power views with constant factor ----------------------------------------- +consPowerView :: View Expr (Expr, (Expr, Expr))+consPowerView = addNegativeView $ addUnitTimesView powerView --- | Power views ---------------------------------------------------------------strictPowerView :: View Expr (Expr, (Expr, Expr))-strictPowerView = strictPowerConsView - <&> (simplePowerView >>^ (,) 1) - <&> negPowerView- where- strictPowerConsView = timesView >>> second simplePowerView- negPowerView = makeView f g- where- f (Negate expr) = do - (c, ax) <- match (strictPowerConsView <&> (simplePowerView >>^ (,) 1)) expr- return (negate c, ax)- f _ = Nothing- g = build strictPowerView +consPowerViewForWith :: Num a => View Expr a -> View Expr b -> a -> View Expr (Expr, b)+consPowerViewForWith va vb a = + addNegativeView $ addUnitTimesView (powerViewForWith va vb a) -strictPowerViewFor :: String -> View Expr (Expr, Expr)-strictPowerViewFor pv = makeView f g+consPowerViewFor :: Expr -> View Expr (Expr, Expr)+consPowerViewFor = consPowerViewForWith identity identity++consPowerViewForVar :: String -> View Expr (Expr, Expr)+consPowerViewForVar = consPowerViewFor . Var++unitPowerViewForVar :: String -> View Expr (Expr, Expr)+unitPowerViewForVar s = makeView f g where f expr = do- (c, (a, x)) <- match strictPowerView expr- guard (Var pv == a)+ (c, (s', x)) <- match unitPowerViewVar expr+ guard $ s == s' return (c, x)- g (c, x) = build strictPowerView (c, (Var pv, x))+ g (c, x) = build unitPowerViewVar (c , (s, x)) -powerConsViewFor :: String -> View Expr (Expr, Rational)-powerConsViewFor pv = timesView >>> second (powerViewFor' pv)+unitPowerViewWith :: View Expr a -> View Expr (Expr, (a, Expr))+unitPowerViewWith v = addNegativeView $ addUnitTimesView $ + powerViewWith v identity <&> (unitTimes v >>^ swap) -powerConsView :: View Expr (String, (Expr, Rational))-powerConsView = makeView f g- where- f expr = do- pv <- selectVar expr- cn <- match (powerConsViewFor pv) expr- return (pv, cn)- g (pv, cn) = build (powerConsViewFor pv) cn- -unitPowerForView :: String -> ViewM Maybe Expr (Expr, Rational)-unitPowerForView pv = powerConsViewFor pv <&> (powerViewFor' pv >>^ (,) 1)+unitPowerViewVar :: View Expr (Expr, (String, Expr))+unitPowerViewVar = unitPowerViewWith varView -unitPowerView :: ViewM Maybe Expr (String, (Expr, Rational))-unitPowerView = unitView <&> negUnitView +-- | Careful! This view will match anything, so use it wise and with care.+unitPowerView :: View Expr (Expr, (Expr, Expr))+unitPowerView = unitPowerViewWith identity++-- | A root view+rootView :: View Expr (Expr, Expr)+rootView = makeView f (uncurry root) where - unitView = powerConsView <&> (powerView >>^ \(pv, n) -> (pv, (1, n))) - negUnitView = makeView f g- where- f (Negate expr) = do - (a, (c, x)) <- match unitView expr- return (a, (negate c, x))- f _ = Nothing- g = build unitView + f expr = do+ (a, (x, y)) <- match (powerView >>> second divView) expr+ guard (x == 1 || x == -1)+ return $ if x == 1 then (a, y) else (a, negate y) -simplePowerView :: View Expr (Expr, Expr)-simplePowerView = makeView f g+-- | only matches sqrt and root+strictRootView :: View Expr (Expr, Expr)+strictRootView = makeView f g where f expr = case expr of- Sym s [a, b] | s == powerSymbol -> return (a, b)+ Sym s [a, b] | isRootSymbol s -> return (a, b)+ Sqrt e -> return (e, 2) _ -> Nothing- g (a, b) = a .^. b + + g (a, b) = if b == 2 then Sqrt a else root a b -powerFactorisationView :: View Expr a -> View Expr (Bool, [Expr])-powerFactorisationView v = productView >>> second (makeView f id)- where- f es = return $ map (\x -> build productView (False, x)) $ factorise es- factorise :: [Expr] -> [[Expr]]- factorise es = maybe [es] split $ findIndex isPower es- where- split i = let (xs, ys) = splitAt (i+1) es in xs : factorise ys- isPower = isJust . match v --- | Root views ---------------------------------------------------------------+-- | Power views -------------------------------------------------------------- -rootView :: View Expr (Expr, Rational)-rootView = makeView f g- where - f expr = case expr of- Sqrt e -> return (e, 2)- Sym s [a, Nat b] | s == rootSymbol -> return (a, toRational b)+strictPowerView :: View Expr (Expr, Expr)+strictPowerView = makeView f (uncurry (.^.))+ where+ f expr = + case expr of+ Sym s [a, b] | isPowerSymbol s -> return (a, b) _ -> Nothing- g (a, b) = if b==2 then Sqrt a else root a (fromRational b) -rootConsView :: View Expr (Expr, (Expr, Rational))-rootConsView = timesView >>> second rootView- <&> (rootView >>^ (,) 1)-+powerView :: View Expr (Expr, Expr)+powerView = makeView f g + where+ f = match ((strictRootView >>^ h) <&> strictPowerView)+ h (a, b) = (a, 1 ./. b)+ g (a, b) = + case b of + (Nat 1 :/: b') -> build strictRootView (a, b')+ _ -> build strictPowerView (a, b) --- | Bastiaan's power views ---------------------------------------------------+powerViewWith :: View Expr a -> View Expr b -> View Expr (a, b)+powerViewWith va vb = powerView >>> first va >>> second vb --- | AG: todo: integrate these views with the views above+powerViewForWith :: Eq a => View Expr a -> View Expr b -> a -> View Expr b+powerViewForWith va vb a = makeView f ((build va a .^.) . build vb)+ where + f expr = do+ (a', b) <- match (powerViewWith va vb) expr+ guard $ a == a'+ return b -natView :: View Expr Int-natView = makeView f fromIntegral- where- f (Nat n) = Just $ fromInteger n- f _ = Nothing+powerViewFor :: Expr -> View Expr Expr+powerViewFor = powerViewForWith identity identity -ratioView :: View Rational (Int, Int)-ratioView = makeView f g+powerFactorView :: (Expr -> Expr -> Bool) -> View Expr (Bool, [Expr])+powerFactorView p = productView >>> second (makeView f id) where- f x = return (fromIntegral (numerator x), fromIntegral (denominator x))- g (n,d) = fromIntegral n % fromIntegral d--powerView :: View Expr (String, Rational)-powerView = powerViewWith rationalView+ f = Just . map (build productView . (,) False) . joinBy p -powerViewWith :: View Expr b -> View Expr (String, b)-powerViewWith v = makeView f g- where - f expr = do- pv <- selectVar expr- n <- match (powerViewForWith' v pv) expr- return (pv, n)- g (pv, n) = build (powerViewForWith' v pv) n- -powerViewFor :: String -> View Expr Int-powerViewFor = powerViewForWith natView-powerViewFor' = powerViewForWith' rationalView+-- | Log views ---------------------------------------------------------------- -powerViewForWith :: Num a => View Expr a -> String -> View Expr a-powerViewForWith v pv = makeView f g- where- f expr = - case expr of- Var s | pv == s -> match v 1- e1 :*: e2 -> liftM2 (+) (f e1) (f e2) - Sym s [e, n] | s == powerSymbol -> do- n'<- match v n- liftM (* n') (f e)- _ -> Nothing- - g a = Var pv .^. build v a- -powerViewForWith' v pv = makeView f g- where- f expr = - case expr of- Var s | pv == s -> match v 1- Sym s [Var s', n] | s' == pv && s == powerSymbol -> match v n+logView :: View Expr (Expr, Expr)+logView = makeView f (uncurry logBase)+ where + f expr = case expr of+ Sym s [a, b] | isLogSymbol s -> return (a, b) _ -> Nothing- - g a = Var pv .^. build v a -powerFactorView :: View Expr (String, Expr, Int)-powerFactorView = powerFactorViewWith identity -powerFactorViewWith :: Num a => View Expr a -> View Expr (String, a, Int)-powerFactorViewWith v = makeView f g- where- f expr = do- pv <- selectVar expr- (e, n) <- match (powerFactorViewForWith pv v) expr- return (pv, e, n)- g (pv, e, n) = build (powerFactorViewForWith pv v) (e, n)--powerFactorViewForWith :: Num a => String -> View Expr a -> View Expr (a, Int)-powerFactorViewForWith pv v = makeView f g- where- f expr = - case expr of- Var s | pv == s -> Just (1, 1)- Negate e -> do- (a, b) <- f e- return (negate a, b)- e1 :*: e2 -> do - (a1, b1) <- f e1- (a2, b2) <- f e2- return (a1*a2, b1+b2)- Sym s [e1, Nat n]- | s == powerSymbol -> do - (a1, b1) <- f e1- a <- match v (build v a1 ^ Nat n)- return (a, b1 * fromInteger n)- _ -> do- guard (pv `notElem` collectVars expr)- a <- match v expr - return (a, 0)- - g (a, b) = build v a .*. (Var pv .^. fromIntegral b)-+-- | Help (non-power) views --------------------------------------------------- --- | Normalising views ---------------------------------------------------------+unitTimes :: Num t => View a b -> View a (t, b)+unitTimes = (>>^ (,) 1) -normPowerNonNegRatio :: View Expr (M.Map String Rational, Rational) -- (Rational, M.Map String Rational)-normPowerNonNegRatio = makeView (liftM swap . f) (g . swap)- where- swap (x,y) = (y,x)- f expr = - case expr of- Sym s [a,b] - | s==powerSymbol -> do- (r, m) <- f a- if r==1 - then do- r2 <- match rationalView b- return (1, M.map (*r2) m)- else do- n <- match integerView b- if n >=0 - then return (r Prelude.^ n, M.map (*fromIntegral n) m)- else return (1/(r Prelude.^ abs n), M.map (*fromIntegral n) m)- | s==rootSymbol ->- f (Sym powerSymbol [a, 1/b])- Sqrt a -> - f (Sym rootSymbol [a,2])- a :*: b -> do- (r1, m1) <- f a- (r2, m2) <- f b- return (r1*r2, M.unionWith (+) m1 m2)- a :/: b -> do- (r1, m1) <- f a- (r2, m2) <- f b- guard (r2 /= 0)- return (r1/r2, M.unionWith (+) m1 (M.map negate m2))- Var s -> return (1, M.singleton s 1)- Nat n -> return (toRational n, M.empty)- Negate x -> do - (r, m) <- f x- return (negate r, m)- _ -> do- r <- match rationalView expr- return (fromRational r, M.empty)- g (r, m) = - let xs = map f (M.toList m)- f (s, r) = Var s .^. fromRational r- in build productView (False, fromRational r : xs)+addTimesView :: View Expr a -> View Expr (Expr, a)+addTimesView v = timesView >>> second v --- | AG: todo: change double to norm view for rationals-normPowerNonNegDouble :: View Expr (Double, M.Map String Rational)-normPowerNonNegDouble = makeView (liftM (roundof 6) . f) g- where- roundof n (x, m) = (fromIntegral (round (x * 10.0 ** n)) / 10.0 ** n, m)- f expr = - case expr of- Sym s [a,b] - | s==powerSymbol -> do- (x, m) <- f a- y <- match rationalView b- return (x ** fromRational y, M.map (*y) m)- | s==rootSymbol -> f (Sym powerSymbol [a, 1/b])- Sqrt a -> f (Sym rootSymbol [a,2])- a :*: b -> do- (r1, m1) <- f a- (r2, m2) <- f b- return (r1*r2, M.unionWith (+) m1 m2)- a :/: b -> do- (r1, m1) <- f a- (r2, m2) <- f b- guard (r2 /= 0)- return (r1/r2, M.unionWith (+) m1 (M.map negate m2))- Var s -> return (1, M.singleton s 1)- Negate x -> do - (r, m) <- f x- return (negate r, m)- _ -> do- d <- match doubleView expr- return (d, M.empty)- g (r, m) = - let xs = map f (M.toList m)- f (s, r) = Var s .^. fromRational r- in build productView (False, fromDouble r : xs)+addUnitTimesView :: View Expr a -> View Expr (Expr, a)+addUnitTimesView v = addTimesView v <&> unitTimes v +negateView :: (Num a, WithFunctions a) => View a a+negateView = makeView isNegate negate -type PowerMap = (M.Map String Rational, Rational)+addNegativeView :: View Expr a -> View Expr a+addNegativeView v = v <&> (negateView >>> v) -normPowerView' :: View Expr [PowerMap]-normPowerView' = makeView (liftM h . f) g+varView :: View Expr String+varView = makeView f Var where- f = (mapM (match normPowerNonNegRatio) =<<) . match sumView- g = build sumView . map (build normPowerNonNegRatio)- h :: [PowerMap] -> [PowerMap]- h = map (foldr1 (\(x,y) (_,q) -> (x,y+q))) . groupBy (\x y -> fst x == fst y) . sort--normPowerView :: View Expr (String, Rational)-normPowerView = makeView f g- where- f expr = - case expr of- Sym s [x,y] - | s==powerSymbol -> do- (s, r) <- f x- r2 <- match rationalView y- return (s, r*r2)- | s==rootSymbol -> - f (x^(1/y))- Sqrt x ->- f (Sym rootSymbol [x, 2])- Var s -> return (s, 1) - x :*: y -> do- (s1, r1) <- f x- (s2, r2) <- f y- guard (s1==s2)- return (s1, r1+r2)- Nat 1 :/: y -> do- (s, r) <- f y- return (s, -r)- x :/: y -> do- (s1, r1) <- f x- (s2, r2) <- f y- guard (s1==s2)- return (s1, r1-r2) - _ -> Nothing- - g (s, r) = Var s .^. fromRational r+ f (Var s) = Just s+ f _ = Nothing
src/Domain/Math/Simplification.hs view
@@ -10,16 +10,19 @@ -- ----------------------------------------------------------------------------- module Domain.Math.Simplification - ( Simplify(..), smartConstructors+ ( Simplify(..), SimplifyConfig(..), smartConstructors+ , simplifyConfig , Simplified, simplified, liftS, liftS2 , simplifyRule+ , mergeAlike, distribution, constantFolding+ , mergeAlikeSum, mergeAlikeProduct ) where import Common.Context import Common.Navigator import Common.Transformation import Common.Uniplate-import Common.View hiding (simplify)+import Common.View hiding (simplify, simplifyWith) import Control.Monad import Data.List import Data.Maybe@@ -29,29 +32,47 @@ import Domain.Math.SquareRoot.Views import Test.QuickCheck import qualified Common.View as View-import Common.Rewriting ++data SimplifyConfig = SimplifyConfig + { withSmartConstructors :: Bool+ , withMergeAlike :: Bool+ , withDistribution :: Bool+ , withSimplifySquareRoot :: Bool+ , withConstantFolding :: Bool+ }+ class Simplify a where+ simplifyWith :: SimplifyConfig -> a -> a simplify :: a -> a+ simplify = simplifyWith simplifyConfig +simplifyConfig :: SimplifyConfig+simplifyConfig = SimplifyConfig True True True True True+ instance Simplify a => Simplify (Context a) where- simplify = change simplify+ simplifyWith cfg = change $ simplifyWith cfg instance Simplify a => Simplify (Equation a) where- simplify = fmap simplify+ simplifyWith cfg = fmap $ simplifyWith cfg +instance Simplify a => Simplify (Relation a) where+ simplifyWith cfg = fmap $ simplifyWith cfg+ instance Simplify a => Simplify [a] where- simplify = fmap simplify+ simplifyWith cfg = fmap $ simplifyWith cfg instance Simplify Expr where- simplify = smartConstructors - . mergeAlike - . distribution - . View.simplify (squareRootViewWith rationalView)- . constantFolding+ simplifyWith cfg = let optional p f = if p then f else id in+ optional (withSmartConstructors cfg) smartConstructors+ . optional (withMergeAlike cfg) mergeAlike+ . optional (withDistribution cfg) distribution+ . optional (withSimplifySquareRoot cfg) (View.simplify + (squareRootViewWith rationalView))+ . optional (withConstantFolding cfg) constantFolding instance Simplify a => Simplify (Rule a) where- simplify = doAfter simplify -- by default, simplify afterwards+ simplifyWith cfg = doAfter (simplifyWith cfg) -- by default, simplify afterwards data Simplified a = S a deriving (Eq, Ord) @@ -93,7 +114,7 @@ acosh = liftS acosh instance Simplify (Simplified a) where- simplify = id+ simplifyWith _ = id instance (Simplify a, IsTerm a) => IsTerm (Simplified a) where toTerm (S x) = toTerm x@@ -127,7 +148,7 @@ Negate a -> neg a a :*: b -> a .*. b a :/: b -> a ./. b- Sym s [a, b] | s == powerSymbol -> + Sym s [a, b] | isPowerSymbol s -> a .^. b _ -> expr @@ -135,8 +156,10 @@ -- Distribution of constants distribution :: Expr -> Expr-distribution = transformTD $ \expr ->- fromMaybe expr $ do+distribution = descend distribution . f+ where+ f expr =+ fromMaybe expr $ case expr of a :*: b -> do (x, y) <- match plusView a@@ -160,8 +183,7 @@ constantFolding expr = case match rationalView expr of Just r -> fromRational r- Nothing -> let (xs, f) = uniplate expr- in f (map constantFolding xs)+ Nothing -> descend constantFolding expr ---------------------------------------------------------------------- -- merge alike for sums and products@@ -177,12 +199,13 @@ mergeAlikeProduct :: [Expr] -> [Expr] mergeAlikeProduct ys = f [ (match rationalView y, y) | y <- ys ]- where f [] = []- f ((Nothing , e):xs) = e:f xs- f ((Just r , _):xs) = - let cs = r : [ c | (Just c , _) <- xs ]- rest = [ x | (Nothing , x) <- xs ]- in build rationalView (product cs):rest+ where + f [] = []+ f ((Nothing , e):xs) = e:f xs+ f ((Just r , _):xs) = + let cs = r : [ c | (Just c, _) <- xs ]+ rest = [ x | (Nothing, x) <- xs ]+ in build rationalView (product cs):rest mergeAlikeSum :: [Expr] -> [Expr] mergeAlikeSum xs = rec [ (Just $ pm 1 x, x) | x <- xs ]@@ -198,10 +221,10 @@ Nothing -> (r, e) rec [] = []- rec ((Nothing, e):xs) = e:rec xs- rec ((Just (r, a), e):xs) = new:rec rest+ rec ((Nothing, e):ys) = e:rec ys+ rec ((Just (r, a), e):ys) = new:rec rest where- (js, rest) = partition (maybe False ((==a) . snd) . fst) xs+ (js, rest) = partition (maybe False ((==a) . snd) . fst) ys rs = r:map fst (mapMaybe fst js) new | null js = e | otherwise = build rationalView (sum rs) .*. a
src/Domain/Math/SquareRoot/Tests.hs view
@@ -15,12 +15,12 @@ import Test.QuickCheck import Domain.Math.Data.SquareRoot import Domain.Math.Numeric.Laws-import Common.Utils ()+import Common.TestSuite ------------------------------------------------------------------- -- Testing -tests :: IO ()+tests :: TestSuite tests = testNumLaws "square roots" squareRootGen -- testFracLaws "square roots" squareRootGen
src/Domain/Math/SquareRoot/Views.hs view
@@ -34,7 +34,7 @@ a :*: b -> liftM2 (*) (f a) (f b) a :/: b -> join $ liftM2 fracDiv (f a) (f b) Sqrt a -> fmap sqrtRational (match rationalView a)- Sym s [a, b] | s == powerSymbol ->+ Sym s [a, b] | isPowerSymbol s -> liftM2 (^) (f a) (match integerView b) _ -> fmap con (match v expr)
− src/Domain/RegularExpr/Definitions.hs
@@ -1,70 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.RegularExpr.Definitions where--import Domain.RegularExpr.Expr-import Common.Uniplate-import Common.Utils (distinct)--deterministic :: (Show a, Eq a) => RE a -> Bool-deterministic r = deterministicSimple r {-- case (deterministicSimple r, det r) of- (b1, b2) | b1==b2 -> b1- _ -> error $ show r -}- -deterministicSimple :: Eq a => RE a -> Bool-deterministicSimple regexp =- distinct (lookahead regexp) && all deterministicSimple (children regexp)--det :: Eq a => RE a -> Bool-det regexp =- case regexp of- EmptySet -> True- Epsilon -> True- Atom _ -> True- Option r -> det (r :|: Epsilon)- Star r -> det r- Plus r -> det (r :*: Star r)- r :|: s -> lookahead r `disj` lookahead s && det r && det s- EmptySet :*: r -> det r- Epsilon :*: r -> det r- Atom _ :*: r -> det r- Option s :*: r -> det ((s :|: Epsilon) :*: r)- Star s :*: r -> lookahead s `disj` lookahead r && det s && det r- Plus s :*: r -> det ((s :*: Star s) :*: r)- (q :|: s) :*: r -> det ((q :*: r) :|: (s :*: r))- (q :*: s) :*: r -> det (q :*: (s :*: r))---disj xs ys = all (`notElem` xs) ys--empty :: RE a -> Bool-empty = foldRE (False, True, const (False), const True, const True, id, (&&), (||))--lookahead :: RE a -> [a]-lookahead = map fst . firsts--firsts :: RE a -> [(a, RE a)]-firsts regexp =- case regexp of- EmptySet -> []- Epsilon -> []- Atom a -> [(a, Epsilon)]- Option r -> firsts r- Star r -> firsts (nonempty r :*: Star r)- Plus r -> firsts (r :*: Star r)- r :*: s -> [ (a, q :*: s) | (a, q) <- firsts r ] ++- (if empty r then firsts s else [])- r :|: s -> firsts r ++ firsts s--nonempty :: RE a -> RE a-nonempty regexp = foldr (:|:) EmptySet [ Atom a :*: r | (a, r) <- firsts regexp ]
− src/Domain/RegularExpr/Exercises.hs
@@ -1,75 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.RegularExpr.Exercises (regexpExercise) where--import Common.Exercise-import Common.Navigator-import Common.Traversable-import Common.Rewriting hiding (difference)-import Domain.RegularExpr.Expr-import Domain.RegularExpr.Parser-import Domain.RegularExpr.Strategy-import Domain.RegularExpr.Definitions-import Control.Monad-import System.Random-import Test.QuickCheck--regexpExercise :: Exercise RegExp-regexpExercise = makeExercise- { description = "Rewrite a regular expression"- , exerciseCode = makeCode "regexp" "normalform"- , status = Experimental- , parser = parseRegExp- , prettyPrinter = ppRegExp--- , equivalence = eqRE- , similarity = equalWith operators -- modulo associativity- , isReady = deterministic- , isSuitable = (>1) . length . crush- , difference = differenceMode eqRE- , strategy = deterministicStrategy- , navigation = navigator--- , extraRules :: [Rule (Context a)] -- Extra rules (possibly buggy) not appearing in strategy- , testGenerator = Just startFormGen -- arbitrary- , randomExercise = simpleGenerator startFormGen -- myGen- , examples = generate 5 (mkStdGen 2805) (replicateM 15 startFormGen)- }---- myGen :: Gen RegExp--- myGen = restrictGenerator (isSuitable regexpExercise) arbitrary--startFormGen :: Gen RegExp-startFormGen = do- i <- oneof $ map return [1..10]- xs <- replicateM i $ do- j <- oneof $ map return [1..5]- ys <- replicateM j $ oneof $ map (return . Atom . return) "abcd"- return $ foldr1 (:*:) ys- return $ foldr1 (:|:) xs ---- equivalence of regular expressions-eqRE :: Eq a => RE a -> RE a -> Bool-eqRE = (==)--{--checkUntil :: Ord a => Int -> RE a -> RE a -> Bool-checkUntil n r s = empty r == empty s && (n==0 || next)- where- make = groupBy eqFst . sortBy cmpFst . firsts- eqFst (a, _) (b, _) = a==b - cmpFst (a, _) (b, _) = compare a b- - as = make r- bs = make s- next = and ((length as == length bs) : zipWith f as bs)- - -- f ((a, _):- f _ _ = False -}
− src/Domain/RegularExpr/Expr.hs
@@ -1,175 +0,0 @@-{-# OPTIONS -XTypeSynonymInstances #-}--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.RegularExpr.Expr where--import Common.Rewriting-import Common.Traversable-import Common.Uniplate-import Control.Monad-import Domain.Math.Expr.Symbolic-import Test.QuickCheck------------------------------------------------------------------------- Data type declaration--infixl 4 :|:-infixl 5 :*:--type RegExp = RE String--data RE a = EmptySet | Epsilon | Atom a | Option (RE a) | Star (RE a)- | Plus (RE a) | RE a :*: RE a | RE a :|: RE a- deriving (Show, Eq, Ord)------------------------------------------------------------------------- Fold--foldRE (es, eps, at, opt, st, pl, sq, ch) = rec - where- rec regexp = - case regexp of- EmptySet -> es- Epsilon -> eps- Atom a -> at a- Option r -> opt (rec r)- Star r -> st (rec r)- Plus r -> pl (rec r)- r :*: s -> sq (rec r) (rec s)- r :|: s -> ch (rec r) (rec s)------------------------------------------------------------------------- General instances--instance Functor RE where- fmap f = foldRE (EmptySet, Epsilon, Atom . f, Option, Star, Plus, (:*:), (:|:))--instance Crush RE where- crush (Atom a) = [a]- crush regexp = concatMap crush (children regexp)--instance Arbitrary RegExp where- arbitrary = sized (arbRE $ oneof $ map return ["a", "b", "c", "d"])-instance CoArbitrary RegExp where- coarbitrary = foldRE - ( variant 0- , variant 1- , \a -> variant 2 . coarbitrary a- , \a -> variant 3 . a- , \a -> variant 4 . a- , \a -> variant 5 . a- , \a b -> variant 6 . a . b- , \a b -> variant 7 . a . b- )--arbRE :: Gen a -> Int -> Gen (RE a)-arbRE g n - | n == 0 = frequency - [ (6, liftM Atom g)- , (3, return Epsilon)- , (1, return EmptySet)- ]- | otherwise = frequency - [ (3, arbRE g 0)- , (2, unop Star) -- (1, unop Option), (1, unop Plus)- , (3, binop (:*:)), (3, binop (:|:))- ]- where- rec = arbRE g (n `div` 2)- unop f = liftM f rec- binop f = liftM2 f rec rec------------------------------------------------------------------------- Pretty-printer--ppRegExp :: RegExp -> String-ppRegExp = ppWith (const id)--ppWith :: (Int -> a -> String) -> RE a -> String-ppWith f = ($ 0) . foldRE - (const "F", const "T", flip f, unop "?", unop "*", unop "+", binop 5 "", binop 4 "|")- where - unop s a _ = parIf False (a 6 ++ s)- binop i s a b n = parIf (n > i) (a i ++ s ++ b i)- parIf b s = if b then "(" ++ s ++ ")" else s----testje = ppWith (const id) (Star (Plus (Atom "P")) :*: (Option (Atom "Q" :*: Option (Atom "S")) :|: Atom "R"))------------------------------------------------------------------------- Function for associative operators--concatOp :: Operator (RE a)-concatOp = associativeOperator (:*:) isConcat- where- isConcat (r :*: s) = Just (r, s)- isConcat _ = Nothing--choiceOp :: Operator (RE a)-choiceOp = associativeOperator (:|:) isChoice- where- isChoice (r :|: s) = Just (r, s)- isChoice _ = Nothing------------------------------------------------------------------------- Instances for rewriting--instance Uniplate (RE a) where- uniplate regexp = - case regexp of- EmptySet -> ([], \[] -> EmptySet)- Epsilon -> ([], \[] -> Epsilon)- Atom a -> ([], \[] -> Atom a)- Option r -> ([r], \[a] -> Option a)- Star r -> ([r], \[a] -> Star a)- Plus r -> ([r], \[a] -> Plus a)- r :*: s -> ([r, s], \[a, b] -> a :*: b)- r :|: s -> ([r, s], \[a, b] -> a :|: b)--instance Eq a => ShallowEq (RE a) where- shallowEq re1 re2 = - case (re1, re2) of- (EmptySet, EmptySet) -> True- (Epsilon, Epsilon ) -> True- (Atom a, Atom b ) -> a==b- (Option _, Option _) -> True- (Star _, Star _ ) -> True- (Plus _, Plus _ ) -> True- (_ :*: _, _ :*: _ ) -> True- (_ :|: _, _ :|: _ ) -> True- _ -> False--instance Different (RE a) where- different = (EmptySet, Epsilon)--instance IsTerm RegExp where - toTerm = foldRE - ( nullary "EmptySet", nullary "Epsilon", variable, unary "Option"- , unary "Star", unary "Plus", binary ":*:", binary ":|:"- ) -- fromTerm a = fromTermWith f a `mplus` liftM Atom (getVariable a)- where- f s [] - | s == "EmptySet" = return EmptySet- | s == "Epsilon" = return Epsilon- f s [x] - | s == "Option" = return (Option x)- | s == "Star" = return (Star x)- | s == "Plus" = return (Plus x)- f s [x, y] - | s == ":*:" = return (x :*: y)- | s == ":|:" = return (x :|: y)- f _ _ = fail "fromExpr"--instance Rewrite RegExp where- operators = [concatOp, choiceOp]- associativeOps = const $ map toSymbol [":*:", ":|:"]
− src/Domain/RegularExpr/Parser.hs
@@ -1,39 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.RegularExpr.Parser (parseRegExp) where--import Domain.RegularExpr.Expr-import Text.Parsing--logicScanner :: Scanner-logicScanner = (specialSymbols "+*?|" defaultScanner)- { keywords = ["T", "F"]- , keywordOperators = ["+", "*", "?", "|"]- , isIdentifierCharacter = const False- }--parseRegExp :: String -> Either String RegExp-parseRegExp = parseWithM logicScanner pRE--pRE :: TokenParser RegExp-pRE = pOr - where- pOr = pChainl ((:|:) <$ pKey "|") pSeq- pSeq = foldl1 (:*:) <$> pList1 pPost- pPost = foldl (flip ($)) <$> pAtom <*> pList pUnop- pUnop = Star <$ pKey "*" <|> Plus <$ pKey "+" <|> Option <$ pKey "?"- pAtom = Atom <$> (pVarid <|> pConid)- <|> Epsilon <$ pKey "T"- <|> EmptySet <$ pKey "F"- <|> pSpec '(' *> pRE <* pSpec ')'---- testje = parseRegExp "P+*((QS?)?|R)"
− src/Domain/RegularExpr/Strategy.hs
@@ -1,100 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Domain.RegularExpr.Strategy (deterministicStrategy) where--import Domain.RegularExpr.Expr-import Common.Context-import Common.Strategy-import Common.Rewriting-import Common.Transformation-import Prelude hiding (repeat, replicate)--deterministicStrategy :: LabeledStrategy (Context RegExp)-deterministicStrategy = label "deterministic and precise" $ - repeat (somewhere- ((liftToContext ruleLeftFactor1 <|> - liftToContext ruleLeftFactor2 <|> - liftToContext ruleIdempOr <|>- -- liftToContext ruleEpsilonSeq <|>- -- liftToContext ruleEmptySeq <|>- -- liftToContext ruleEmptyChoice <|>- liftToContext ruleDefOption) |>- liftToContext ruleCommFactor))- <*> - repeat (somewhere (liftToContext ruleIntroOption))--ruleLeftFactor1 :: Rule RegExp-ruleLeftFactor1 = rule "LeftFactor1" $ \a x y -> - (a :*: x) :|: (a :*: y) :~> a :*: (x :|: y)- -ruleLeftFactor2 :: Rule RegExp-ruleLeftFactor2 = ruleList "LeftFactor2" $- [ \a x -> (a :*: x) :|: a :~> a :*: Option x- , \a x -> a :|: (a :*: x) :~> a :*: Option x- ]--ruleIdempOr :: Rule RegExp-ruleIdempOr = rule "IdempOr" $ \a -> - a :|: a :~> a- -ruleCommFactor :: Rule RegExp-ruleCommFactor = ruleList "CommFactor"- [ \a b _ _ -> a :|: b :|: a :~> a :|: a :|: b- , \a b x _ -> (a :*: x) :|: b :|: a :~> (a :*: x) :|: a :|: b- , \a b y _ -> a :|: b :|: (a :*: y) :~> a :|: (a :*: y) :|: b- , \a b x y -> (a :*: x) :|: b :|: (a :*: y) :~> (a :*: x) :|: (a :*: y) :|: b- ]- -- -ruleDefOption :: Rule RegExp-ruleDefOption = rule "DefOption" $ \a ->- Option a :~> Epsilon :|: a- -ruleIntroOption :: Rule RegExp-ruleIntroOption = ruleList "IntroOption" - [ \a -> Epsilon :|: a :~> Option a- , \a -> a :|: Epsilon :~> Option a- ]- -----{--ruleEpsilonSeq :: Rule RegExp-ruleEpsilonSeq = ruleList "EpsilonSeq" - [ \a -> Epsilon :*: a :~> a- , \a -> a :*: Epsilon :~> a- ]- -ruleEmptySeq :: Rule RegExp-ruleEmptySeq = ruleList "EmptySeq" - [ \a -> EmptySet :*: a :~> EmptySet- , \a -> a :*: EmptySet :~> EmptySet- ]- -ruleEmptyChoice :: Rule RegExp-ruleEmptyChoice = ruleList "EmptyChoice" - [ \a -> EmptySet :|: a :~> a- , \a -> a :|: EmptySet :~> a- ]--} -------------------{--ruleComm :: Rule RegExp-ruleComm = makeSimpleRuleList "Comm" $ \re -> do- let xs = collectWithOperator choiceOp re- i <- [0..length xs-1]- j <- [i+2..length xs-1]- let (as, b:bs) = splitAt i xs- (cs, d:ds) = splitAt (j-i-1) bs- guard (all (`notElem` (lookahead b)) (lookahead d))- let new = as++[b,d]++cs++ds- return (buildWithOperator choiceOp new)-}
src/Domain/RelationAlgebra.hs view
@@ -30,7 +30,7 @@ import Test.QuickCheck import System.Random import Data.List -import Common.Apply +import Common.Classes import Common.Context import Control.Monad
− src/Domain/RelationAlgebra/Equivalence.hs
@@ -1,189 +0,0 @@------------------------------------------------------------------------------ --- Copyright 2010, 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 Domain.RelationAlgebra.Equivalence (isEquivalent) where - -import Data.List -import Data.Maybe -import Domain.RelationAlgebra.Formula -import Common.Apply -import Common.Context -import Domain.RelationAlgebra.Strategies -{- -infixr 1 :.: -infixr 2 :+: -infixr 3 :||: -infixr 4 :&&: --} -{- --- | The data type RelAlg is the abstract syntax for the domain --- | of logic expressions. -data RelAlg = Var String - | RelAlg :.: RelAlg -- composition - | RelAlg :+: RelAlg -- relative addition - | RelAlg :&&: RelAlg -- and (conjunction) - | RelAlg :||: RelAlg -- or (disjunction) - | Not RelAlg -- not - | Inv RelAlg -- inverse - | U -- universe - | E -- empty - deriving (Show, Eq, Ord) - -------------------------------------- - - -isAtom :: RelAlg -> Bool -isAtom r = - case r of - Var x -> True - Not (Var x) -> True - Inv (Var x) -> True - Not (Inv (Var x)) -> True - U -> True - E -> True - otherwise -> False - -isMolecule :: RelAlg -> Bool -isMolecule (r :.: s) = isMolecule r && isMolecule s -isMolecule (r :+: s) = isMolecule r && isMolecule s -isMolecule r = isAtom r - -isDisj :: RelAlg -> Bool -isDisj (r :||: s) = isDisj r && isDisj s -isDisj r = isMolecule r - -isCNF :: RelAlg -> Bool -isCNF (r :&&: s) = isCNF r && isCNF s -isCNF r = isDisj r --} --- | maak er een cnf van -isEquivalent :: RelAlg -> RelAlg -> Bool -isEquivalent x1 x2 = - let res1 = fromContext (applyD toCNF (inContext x1)) -- cnf van x1 - res2 = fromContext (applyD toCNF (inContext x2)) -- cnf van x2 - mols = union (getSetOfMolecules res1) (getSetOfMolecules res2) - (rs, r1, r2) = remCompls mols res1 res2 - vals = createValuations rs - in all (\ v -> evalFormula r1 v == evalFormula r2 v) vals -{- --- | zet 'm in cnf -solve (Inv (Inv (Not (Var "p")) :+: Not (Var "q"))) = Not (Var "p") :+: Not (Inv (Var "q")) -solve (Not (Not (Var "p") :+: Not (Inv (Var "q")))) = undefined -solve (Not (Inv (Inv (Not (Var "p")) :+: Not (Var "q")))) = undefined -solve (Not (Var "p" :.: Inv (Var "q"))) = Inv (Inv (Not (Var "p")) :+: Not (Var "q")) -solve x = x --} - -{- -ra1 = Var "a" :||: (Var "p" :.: Inv (Var "q")) -ra2 = Var "a" :||: (Inv (Inv (Not (Var "p")) :+: Not (Var "q"))) - -fa1 = Inv (Var "r" :+: Var "s") -fa2 = Inv (Var "s") :+: Inv (Var "r") --} - -{- -mols = union (getSetOfMolecules ra1) (getSetOfMolecules ra2) -triple@(t1, t2, t3) = remCompls mols ra1 ra2 -vs = createValuations t1 -bb = and (map (\v -> evalFormula ra1 v == evalFormula ra2 v) vs) --} -remCompls :: [RelAlg] -> RelAlg -> RelAlg -> ([RelAlg], RelAlg, RelAlg) -remCompls rs r1 r2 = - let complements = searchForComplements rs - -- sub = [ (r1, Not r2) | (r1, r2) <- complements ] - in ( removeCompls rs complements - , substCompls r1 complements - , substCompls r2 complements - ) - --- | -substCompls :: RelAlg -> [(RelAlg, RelAlg)] -> RelAlg -substCompls = foldl subst - - - -subst :: RelAlg -> (RelAlg, RelAlg) -> RelAlg -subst r (r1, r2) = - case r of - p :&&: q -> subst p (r1, r2) :&&: subst q (r1, r2) - p :||: q -> subst p (r1, r2) :||: subst q (r1, r2) - _ -> if r == r1 - then Not r2 - else r - - -removeCompls :: [RelAlg] -> [(RelAlg, RelAlg)] -> [RelAlg] -removeCompls xs ys = [ x | x <- xs, notElem x (map snd ys)] - --- | Search for complements -searchForComplements ::[RelAlg] -> [(RelAlg, RelAlg)] -searchForComplements [] = [] -searchForComplements (x:xs) = [(x,z) | z <- xs, isComplement x z] ++ searchForComplements xs - -isComplement :: RelAlg -> RelAlg -> Bool -isComplement = (==) . fromContext . applyD toCNF . inContext . Not - --- FIXME: what should we do with the identity relation? -evalFormula :: RelAlg -> [(RelAlg, Bool)] -> Bool -evalFormula f val = - case lookup f val of - Just b -> b - Nothing -> - case f of - f1 :&&: f2 -> evalFormula f1 val && evalFormula f2 val - f1 :||: f2 -> evalFormula f1 val || evalFormula f2 val - Not f -> not (evalFormula f val) - V -> True - E -> False - x -> let value = lookup x val - in if value == Nothing - then error $ "evalFormula: molecule not in valuation " ++ show (f, val) - else fromJust value - - - - - - --- | Get the set of molecules of an expression in CNF as list. -getSetOfMolecules :: RelAlg -> [RelAlg] -getSetOfMolecules = nub . getMolecules - where - getMolecules :: RelAlg -> [RelAlg] - getMolecules expr = - case expr of - p :&&: q -> getMolecules p ++ getMolecules q - p :||: q -> getMolecules p ++ getMolecules q - Not p -> getMolecules p - V -> [] - E -> [] - I -> [] - p -> [p] - - -{------------------------------------------------------------------- - Given a varList, for example [x,y], function createValuations - creates all valuations: - [[(x,0),(y,0)],[(x,0),(y,1)],[(x,1),(y,0)],[(x,1),(y,1)]], - where (x,0) means: variable x equals 0. ---------------------------------------------------------------------} - -type Molecule = RelAlg --- type Valuation = (RelAlg, Bool) - - -createValuations :: [a] -> [[(a, Bool)]] -createValuations = foldr op [[]] - where op a vs = [ (a, b):v | v <- vs, b <- [True, False] ] - -prop :: RelAlg -> RelAlg -> Bool -prop p q = isEquivalent p q == probablyEqual p q
src/Domain/RelationAlgebra/Exercises.hs view
@@ -17,7 +17,7 @@ import Domain.RelationAlgebra.Strategies import Domain.RelationAlgebra.Rules import Domain.RelationAlgebra.Parser -import Common.Apply +import Common.Classes import Common.Exercise import Common.Context import Data.Maybe @@ -29,8 +29,8 @@ cnfExercise :: Exercise RelAlg cnfExercise = testableExercise - { description = "To conjunctive normal form" - , exerciseCode = makeCode "relationalg" "cnf" + { exerciseId = describe "To conjunctive normal form" $ + newId "relationalgebra.cnf" , status = Alpha , parser = parseRelAlg , prettyPrinter = ppRelAlg
src/Domain/RelationAlgebra/Formula.hs view
@@ -11,16 +11,15 @@ ----------------------------------------------------------------------------- module Domain.RelationAlgebra.Formula where -import Domain.Math.Expr.Symbolic -import Common.Exercise (generate) import Common.Uniplate (Uniplate(..)) import Common.Rewriting import Common.Utils import Control.Monad import Data.List import qualified Data.Set as S -import System.Random (StdGen, mkStdGen, split) +import System.Random (StdGen, mkStdGen, split, randomR) import Test.QuickCheck +import Test.QuickCheck.Gen infixr 2 :.: infixr 3 :+: @@ -79,7 +78,7 @@ -- | foldRelAlg is the standard folfd for RelAlg. foldRelAlg :: RelAlgAlgebra a -> RelAlg -> a -foldRelAlg (var, comp, add, conj, disj, not, inverse, universe, ident) = rec +foldRelAlg (var, comp, add, conj, disj, neg, inv, univ, ident) = rec where rec term = case term of @@ -88,26 +87,27 @@ p :+: q -> rec p `add` rec q p :&&: q -> rec p `conj` rec q p :||: q -> rec p `disj` rec q - Not p -> not (rec p) - Inv p -> inverse (rec p) - V -> universe + Not p -> neg (rec p) + Inv p -> inv (rec p) + V -> univ I -> ident type Relation a = S.Set (a, a) evalRelAlg :: Ord a => (String -> Relation a) -> [a] -> RelAlg -> Relation a -evalRelAlg var as = foldRelAlg (var, comp, add, conj, disj, not, inverse, universe, ident) +evalRelAlg var as = foldRelAlg (var, comp, add, conj, disj, neg, inv, univ, ident) where pairs = cartesian as as + comp p q = let f (a1, a2) c = (a1, c) `S.member` p && (c, a2) `S.member` q in S.fromAscList [ x | x <- pairs, any (f x) as ] add p q = let f (a1, a2) c = (a1, c) `S.member` p || (c, a2) `S.member` q in S.fromAscList [ x | x <- pairs, all (f x) as ] conj = S.intersection disj = S.union - not p = S.fromAscList [ x | x <- pairs, x `S.notMember` p ] - inverse = S.map (\(x, y) -> (y, x)) - universe = S.fromAscList pairs + neg p = S.fromAscList [ x | x <- pairs, x `S.notMember` p ] + inv = S.map (\(x, y) -> (y, x)) + univ = S.fromAscList pairs ident = S.fromAscList [ (x, x) | x <- as ] -- | Try to find a counter-example showing that the two formulas are not equivalent. @@ -118,16 +118,17 @@ probablyEqualWith rng p q = all (\i -> eval i p == eval i q) (makeRngs 50 rng) where -- size of (co-)domain - as = [0..1] + as :: [Int] + as = [0..1] -- number of attemps (with different randomly generated relations) + makeRngs :: Int -> StdGen -> [StdGen] makeRngs n g | n == 0 = [] | otherwise = let (g1, g2) = split g in g1 : makeRngs (n-1) g2 - eval g = evalRelAlg (generate 100 g (arbRelations as)) as - -inspect :: [Int] -inspect = map f [1..100] - where f i = S.size $ generate 100 (mkStdGen i) (arbRelations [0..9]) "p" + eval g = + let MkGen f = arbRelations as + (size, a) = randomR (0, 100) g + in evalRelAlg (f a size) as arbRelations :: Eq a => [a] -> Gen (String -> Relation a) arbRelations as = promote (\s -> coarbitrary s (arbRelation as)) @@ -168,42 +169,44 @@ Not s -> ([s], \[a] -> Not a) Inv s -> ([s], \[a] -> Inv a) _ -> ([], \[] -> term) - -instance ShallowEq RelAlg where - shallowEq expr1 expr2 = - case (expr1, expr2) of - (Var a , Var b ) -> a==b - (_ :.: _ , _ :.: _ ) -> True - (_ :+: _ , _ :+: _ ) -> True - (_ :&&: _, _ :&&: _) -> True - (_ :||: _, _ :||: _) -> True - (Not _ , Not _ ) -> True - (Inv _ , Inv _ ) -> True - (V , V ) -> True - (I , I ) -> True - _ -> False instance Different RelAlg where different = (V, I) - + --(var, comp, add, conj, disj, not, inverse, universe, ident) instance IsTerm RelAlg where toTerm = foldRelAlg - ( variable, binary ".", binary "+", binary "&&", binary "||" - , unary "~", unary "-", nullary "V", nullary "I" + ( variable, binary compSymbol, binary addSymbol + , binary conjSymbol + , binary disjSymbol, unary notSymbol, unary invSymbol + , symbol universeSymbol, symbol identSymbol ) fromTerm a = fromTermWith f a `mplus` liftM Var (getVariable a) where f s [] - | s == "V" = return V - | s == "I" = return I + | s == universeSymbol = return V + | s == identSymbol = return I f s [x] - | s == "~" = return (Not x) - | s == "-" = return (Inv x) + | s == notSymbol = return (Not x) + | s == invSymbol = return (Inv x) f s [x, y] - | s == "." = return (x :.: y) - | s == "+" = return (x :+: y) - | s == "&&" = return (x :&&: y) - | s == "||" = return (x :||: y) - f _ _ = fail "fromTerm"+ | s == compSymbol = return (x :.: y) + | s == addSymbol = return (x :+: y) + | s == conjSymbol = return (x :&&: y) + | s == disjSymbol = return (x :||: y) + f _ _ = fail "fromTerm" + +compSymbol, addSymbol, conjSymbol, disjSymbol, + notSymbol, invSymbol, universeSymbol, identSymbol :: Symbol +compSymbol = relalgSymbol "comp" +addSymbol = relalgSymbol "add" +conjSymbol = relalgSymbol "conj" +disjSymbol = relalgSymbol "disj" +notSymbol = relalgSymbol "not" +invSymbol = relalgSymbol "inv" +universeSymbol = relalgSymbol "universe" +identSymbol = relalgSymbol "ident" + +relalgSymbol :: String -> Symbol +relalgSymbol a = newSymbol ["relalg", a]
src/Domain/RelationAlgebra/Generator.hs view
@@ -23,18 +23,18 @@ instance CoArbitrary RelAlg where coarbitrary term = case term of - Var x -> variant 0 . coarbitrary x - p :.: q -> variant 1 . coarbitrary p . coarbitrary q - p :+: q -> variant 2 . coarbitrary p . coarbitrary q - p :&&: q -> variant 3 . coarbitrary p . coarbitrary q - p :||: q -> variant 4 . coarbitrary p . coarbitrary q - Not p -> variant 5 . coarbitrary p - Inv p -> variant 6 . coarbitrary p - V -> variant 7 - I -> variant 8 + Var x -> variant (0 :: Int) . coarbitrary x + p :.: q -> variant (1 :: Int) . coarbitrary p . coarbitrary q + p :+: q -> variant (2 :: Int) . coarbitrary p . coarbitrary q + p :&&: q -> variant (3 :: Int) . coarbitrary p . coarbitrary q + p :||: q -> variant (4 :: Int) . coarbitrary p . coarbitrary q + Not p -> variant (5 :: Int) . coarbitrary p + Inv p -> variant (6 :: Int) . coarbitrary p + V -> variant (7 :: Int) + I -> variant (8 :: Int) arbRelAlg :: Int -> Gen RelAlg -arbRelAlg 0 = frequency [(8, liftM Var (oneof $ map return vars)), (1, return V), (1, return empty), (1, return I)] +arbRelAlg 0 = frequency [(8, liftM Var (oneof $ map return relAlgVars)), (1, return V), (1, return empty), (1, return I)] arbRelAlg n = oneof [ arbRelAlg 0, binop (:.:), binop (:+:), binop (:&&:), binop (:||:) , unop Not, unop Inv ] @@ -43,12 +43,18 @@ unop op = liftM op rec rec = arbRelAlg (n `div` 2) -vars :: [String] -vars = ["q", "r", "s"] +relAlgVars :: [String] +relAlgVars = ["q", "r", "s"] ------------------------------------------------------------------- -- Templates +template1, template2, template3, template4, template7, template8 :: + RelAlg -> RelAlg -> RelAlg -> RelAlg + +template5 :: RelAlg -> RelAlg -> RelAlg -> RelAlg -> RelAlg +template6 :: Maybe RelAlg -> RelAlg -> RelAlg -> Maybe RelAlg -> RelAlg + template1 x y z = x :||: (y :&&: z) template2 x y z = Not(x :&&: (y :||: z)) template3 x y z = Inv(x :||: (y :&&: z)) @@ -77,7 +83,10 @@ gen8 = use3 template2 arbInvNotMol hulpgen1 arbInvNotMol gen9 = use3 template8 hulpgen2 arbInvNotMol arbInvNotMol +use3 :: (a -> b -> c -> d) -> (t -> Gen a) -> (t -> Gen b) -> (t -> Gen c) -> t -> Gen d use3 temp f g h n = liftM3 temp (f n) (g n) (h n) + +use4 :: (a -> b -> c -> d -> e) -> (t -> Gen a) -> (t -> Gen b) -> (t -> Gen c) -> (t -> Gen d) -> t -> Gen e use4 temp f g h k n = liftM4 temp (f n) (g n) (h n) (k n) hulpgen1 :: Int -> Gen RelAlg @@ -87,7 +96,7 @@ hulpgen2 n = liftM3 template7 (arbInvNotMol 1) (arbRelAlg n) (arbRelAlg n) arbInvNotMol :: Int -> Gen RelAlg -arbInvNotMol 0 = frequency [(10, liftM Var (oneof $ map return vars)), (1, return V), (1, return empty), (1, return I)] +arbInvNotMol 0 = frequency [(10, liftM Var (oneof $ map return relAlgVars)), (1, return V), (1, return empty), (1, return I)] arbInvNotMol n = frequency [ (10, arbInvNotMol 0), (4, binop (:.:)), (4, binop (:+:)), (2, unop Not), (2, unop Inv) ] where binop op = liftM2 op rec rec @@ -98,4 +107,4 @@ arbMaybeInvNotMol n = frequency [(3, liftM Just (arbInvNotMol n)), (1, return Nothing)] arbVar :: Gen RelAlg -arbVar = liftM Var (oneof $ map return vars)+arbVar = liftM Var (oneof $ map return relAlgVars)
src/Domain/RelationAlgebra/Parser.hs view
@@ -28,6 +28,7 @@ , (NoMix, [(compSym, (:.:)), (addSym, (:+:))]) ] +andSym, orSym, addSym, compSym, notSym, invSym :: String andSym = "/\\" orSym = "\\/" addSym = "!" @@ -77,8 +78,11 @@ ppRelAlg = ppRelAlgPrio (0, "") ppRelAlgPrio :: (Int, String) -> RelAlg -> String -ppRelAlgPrio n p = foldRelAlg (var, binop 4 ";", binop 4 "!", binop 3 "/\\", binop 2 "\\/", nott, inv, var "V", var "I") p n "" +ppRelAlgPrio = (\f n -> f n "") . flip (foldRelAlg alg) where + alg = (var, binop 4 ";", binop 4 "!", binop 3 "/\\", binop 2 "\\/" + , nott, inv, var "V", var "I" + ) binop prio op p q (n, parent) = parIf (n > prio || (prio==4 && n==4 && op/=parent)) (p (prio+1, op) . ((" "++op++" ")++) . q (prio, op)) var = const . (++)
src/Domain/RelationAlgebra/Rules.hs view
@@ -13,8 +13,10 @@ import Domain.RelationAlgebra.Formula import Domain.RelationAlgebra.Generator() -import Common.Transformation +import Common.Id +import Common.Transformation (Rule, addRuleToGroup, buggyRule) import Common.Rewriting +import qualified Common.Transformation as Rule invRules :: [Rule RelAlg] invRules = [ ruleInvOverUnion, ruleInvOverIntersec, ruleInvOverComp @@ -38,10 +40,21 @@ , buggyRuleAssoc, buggyRuleInvOverComp, buggyRuleInvOverAdd , buggyRuleCompOverIntersec, buggyRuleAddOverUnion, buggyRuleRemCompl ] + +relalg :: IsId a => a -> Id +relalg = (#) "relationalgebra" + +rule :: (RuleBuilder f a, Rewrite a) => String -> f -> Rule a +rule = Rule.rule . relalg + +ruleList :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +ruleList = Rule.ruleList . relalg -- | 1. Alle ~ operatoren naar binnen verplaatsen -conversionGroup s = addRuleToGroup "Conversion" . rule s +conversionGroup :: (RuleBuilder f a, Rewrite a) => String -> f -> Rule a +conversionGroup s = + addRuleToGroup (relalg "Conversion") . rule s ruleInvOverUnion :: Rule RelAlg ruleInvOverUnion = conversionGroup "InvOverUnion" $ @@ -71,8 +84,9 @@ -- | 2. Alle ; en + operatoren zoveel mogelijk naar binnen verplaatsen - -distributionGroup s = addRuleToGroup "Distribution" . ruleList s +distributionGroup :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +distributionGroup s = + addRuleToGroup (relalg "Distribution") . ruleList s ruleCompOverUnion :: Rule RelAlg ruleCompOverUnion = distributionGroup "CompOverUnion" @@ -106,7 +120,9 @@ -- | 4. De Morgan rules -deMorganGroup s = addRuleToGroup "DeMorgan" . rule s +deMorganGroup :: (RuleBuilder f a, Rewrite a) => String -> f -> Rule a +deMorganGroup s = + addRuleToGroup (relalg "DeMorgan") . rule s ruleDeMorganOr :: Rule RelAlg ruleDeMorganOr = deMorganGroup "DeMorganOr" $ @@ -118,7 +134,9 @@ -- | 5. Idempotency -idempotencyGroup s = addRuleToGroup "Idempotency" . rule s +idempotencyGroup :: (RuleBuilder f a, Rewrite a) => String -> f -> Rule a +idempotencyGroup s = + addRuleToGroup (relalg "Idempotency") . rule s ruleIdempOr :: Rule RelAlg ruleIdempOr = idempotencyGroup "IdempotencyOr" $ @@ -130,7 +148,9 @@ -- | 6. Complement -complementGroup s = addRuleToGroup "Complement" . ruleList s +complementGroup :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +complementGroup s = + addRuleToGroup (relalg "Complement") . ruleList s ruleDoubleNegation :: Rule RelAlg ruleDoubleNegation = complementGroup "DoubleNegation" @@ -159,7 +179,9 @@ -- | 7. Absorption complement -absorptionGroup s = addRuleToGroup "Absorption" . ruleList s +absorptionGroup :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +absorptionGroup s = + addRuleToGroup (relalg "Absorption") . ruleList s ruleAbsorpCompl :: Rule RelAlg ruleAbsorpCompl = absorptionGroup "AbsorpCompl" @@ -187,7 +209,9 @@ -- | 8. Remove redundant expressions -simplificationGroup s = addRuleToGroup "Simplification" . ruleList s +simplificationGroup :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +simplificationGroup s = + addRuleToGroup (relalg "Simplification") . ruleList s ruleRemRedunExprs :: Rule RelAlg ruleRemRedunExprs = simplificationGroup "RemRedunExprs" @@ -220,7 +244,9 @@ -- Buggy rules: -buggyGroup s = addRuleToGroup "Buggy" . buggyRule . ruleList s +buggyGroup :: (RuleBuilder f a, Rewrite a) => String -> [f] -> Rule a +buggyGroup s = addRuleToGroup (relalg "Buggy") . buggyRule + . Rule.ruleList ("relationalgebra.buggy." ++ s) buggyRuleIdemComp :: Rule RelAlg buggyRuleIdemComp = buggyGroup "IdemComp" @@ -233,7 +259,7 @@ ] buggyRuleDeMorgan :: Rule RelAlg -buggyRuleDeMorgan = buggyGroup "BuggyDeMorgan" +buggyRuleDeMorgan = buggyGroup "DeMorgan" [ \q r -> Not (q :&&: r) :~> Not q :||: r , \q r -> Not (q :&&: r) :~> q :||: Not r , \q r -> Not (q :&&: r) :~> Not (Not q :||: Not r) @@ -243,7 +269,7 @@ ] buggyRuleNotOverAdd :: Rule RelAlg -buggyRuleNotOverAdd = buggyGroup "BuggyNotOverAdd" +buggyRuleNotOverAdd = buggyGroup "NotOverAdd" [ \q r -> Not (q :+: r) :~> Not q :+: Not r , \q r -> Not (q :+: r) :~> Not q :.: r , \q r -> Not (q :+: r) :~> Not q :+: r @@ -251,7 +277,7 @@ ] buggyRuleNotOverComp :: Rule RelAlg -buggyRuleNotOverComp = buggyGroup "BuggyNotOverComp" +buggyRuleNotOverComp = buggyGroup "NotOverComp" [ \q r -> Not (q :.: r) :~> Not q :.: Not r , \q r -> Not (q :.: r) :~> Not q :.: r , \q r -> Not (q :.: r) :~> Not q :+: r @@ -259,7 +285,7 @@ ] buggyRuleParenth :: Rule RelAlg -buggyRuleParenth = buggyGroup "BuggyParenth" +buggyRuleParenth = buggyGroup "Parenth" [ \q r -> Not (q :&&: r) :~> Not q :&&: r , \q r -> Not (q :||: r) :~> Not q :||: r , \q r -> Not (Not q :&&: r) :~> q :&&: r @@ -275,7 +301,7 @@ ] buggyRuleAssoc :: Rule RelAlg -buggyRuleAssoc = buggyGroup "BuggyAssoc" +buggyRuleAssoc = buggyGroup "Assoc" [ \q r s -> q :||: (r :&&: s) :~> (q :||: r) :&&: s , \q r s -> (q :||: r) :&&: s :~> q :||: (r :&&: s) , \q r s -> (q :&&: r) :||: s :~> q :&&: (r :||: s) @@ -291,28 +317,28 @@ ] buggyRuleInvOverComp :: Rule RelAlg -buggyRuleInvOverComp = buggyGroup "BuggyInvOverComp" +buggyRuleInvOverComp = buggyGroup "InvOverComp" [ \r s -> Inv (r :.: s) :~> Inv r :.: Inv s ] buggyRuleInvOverAdd :: Rule RelAlg -buggyRuleInvOverAdd = buggyGroup "BuggyInvOverAdd" +buggyRuleInvOverAdd = buggyGroup "InvOverAdd" [ \r s -> Inv (r :+: s) :~> Inv r :+: Inv s ] buggyRuleCompOverIntersec :: Rule RelAlg -buggyRuleCompOverIntersec = buggyGroup "BuggyCompOverIntersec" +buggyRuleCompOverIntersec = buggyGroup "CompOverIntersec" [ \q r s -> q :.: (r :&&: s) :~> (q :.: r) :&&: (q :.: s) --alleen toegestaan als q een functie is! , \q r s -> (q :&&: r) :.: s :~> (q :.: s) :&&: (r :.: s) --idem ] buggyRuleAddOverUnion :: Rule RelAlg -buggyRuleAddOverUnion = buggyGroup "BuggyAddOverUnion" +buggyRuleAddOverUnion = buggyGroup "AddOverUnion" [ \q r s -> q :+: (r :||: s) :~> (q :+: r) :||: (q :+: s) --alleen toegestaan als q een functie is! , \q r s -> (q :||: r) :+: s :~> (q :+: s) :||: (r :+: s) --idem ] buggyRuleRemCompl :: Rule RelAlg -buggyRuleRemCompl = buggyGroup "BuggyRemCompl" +buggyRuleRemCompl = buggyGroup "RemCompl" [ \r -> r :&&: Not r :~> V , \r -> Not r :&&: r :~> V , \r -> r :||: Not r :~> empty
src/Main.hs view
@@ -19,7 +19,7 @@ import Data.IORef import Data.Time import Documentation.Make -import Main.ExerciseList +import Main.IDEAS import Main.LoggingDatabase import Main.Options import Network.CGI @@ -48,7 +48,8 @@ -- documentation mode _ | documentationMode flags -> useIDEAS $ - mapM_ makeDocumentation (docItems flags) + let f = makeDocumentation (docDir flags) (testDir flags) + in mapM_ f (docItems flags) -- cgi binary Nothing -> runCGI $ do
− src/Main/ExerciseList.hs
@@ -1,100 +0,0 @@--------------------------------------------------------------------------------- Copyright 2010, 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 Main.ExerciseList (packages, useIDEAS) where--import Common.Utils (Some(..), fromShowString)-import Common.Rewriting-import Domain.Math.Expr-import Service.ExercisePackage-import Service.FeedbackText-import Service.DomainReasoner-import qualified Domain.LinearAlgebra as LA-import qualified Domain.Logic as Logic-import qualified Domain.Logic.FeedbackText 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-import qualified Domain.Math.Polynomial.IneqExercises as Math-import qualified Domain.RegularExpr.Exercises as RE-import qualified Domain.Math.Power.Exercises as Math-import Main.Options-import Service.ServiceList--packages :: [Some ExercisePackage]-packages =- [ -- logic and relation-algebra- Some (package Logic.dnfExercise)- { withOpenMath = True- , toOpenMath = termToOMOBJ . toTerm . fmap (Var . fromShowString)- , fromOpenMath = (>>= fromTerm) . omobjToTerm- , getExerciseText = Just logicText- }- , Some (package Logic.dnfUnicodeExercise)- { withOpenMath = True- , toOpenMath = termToOMOBJ . toTerm . fmap (Var . fromShowString)- , fromOpenMath = (>>= fromTerm) . omobjToTerm- , getExerciseText = Just logicText- }- , somePackage RA.cnfExercise- -- basic math- , someTermPackage Math.naturalExercise- , someTermPackage Math.integerExercise- , someTermPackage Math.rationalExercise- , someTermPackage Math.fractionExercise- , someTermPackage Math.coverUpExercise- , someTermPackage Math.linearExercise- , someTermPackage Math.linearMixedExercise- , someTermPackage Math.quadraticExercise- , someTermPackage Math.higherDegreeExercise- , someTermPackage Math.findFactorsExercise- , someTermPackage Math.ineqLinearExercise- , someTermPackage Math.ineqQuadraticExercise- , someTermPackage Math.ineqHigherDegreeExercise- , someTermPackage Math.quadraticNoABCExercise- , someTermPackage Math.quadraticWithApproximation- , someTermPackage Math.derivativeExercise- , someTermPackage Math.simplifyPowerExercise- , someTermPackage Math.powerOfExercise - , someTermPackage Math.nonNegExpExercise- , someTermPackage Math.calcPowerExercise- -- linear algebra- , someTermPackage LA.gramSchmidtExercise- , someTermPackage LA.linearSystemExercise- , someTermPackage LA.gaussianElimExercise- , someTermPackage LA.systemWithMatrixExercise- -- regular expressions- , somePackage RE.regexpExercise- ]- -logicText :: ExerciseText Logic.SLogic-logicText = ExerciseText- { ruleText = Logic.ruleText- , appliedRule = Logic.appliedRule- , feedbackSyntaxError = Logic.feedbackSyntaxError- , feedbackSame = Logic.feedbackSame- , feedbackBuggy = Logic.feedbackBuggy- , feedbackNotEquivalent = Logic.feedbackNotEquivalent- , feedbackOk = Logic.feedbackOk- , feedbackDetour = Logic.feedbackDetour- , feedbackUnknown = Logic.feedbackUnknown- }- -useIDEAS :: DomainReasoner a -> IO a-useIDEAS action = runDomainReasoner $ do- setVersion shortVersion- setFullVersion fullVersion- addPackages packages- addServices serviceList- addPkgService exerciselistS- action
+ src/Main/IDEAS.hs view
@@ -0,0 +1,130 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Main.IDEAS (useIDEAS) where++import Common.Rewriting+import Common.Utils (Some(..), fromShowString)+import Main.Options+import Service.DomainReasoner+import Service.ExercisePackage+import Service.ServiceList+import qualified Domain.LinearAlgebra as LA+import qualified Domain.LinearAlgebra.Checks as LA+import qualified Domain.Logic as Logic+import qualified Domain.Logic.FeedbackText as Logic+import qualified Domain.Math.Expr as Math+import qualified Domain.Math.Data.Interval as MathInterval+import qualified Domain.Math.Derivative.Exercises as Math+import qualified Domain.Math.Equation.CoverUpExercise as Math+import qualified Domain.Math.Numeric.Exercises as Math+import qualified Domain.Math.Numeric.Tests as MathNum+import qualified Domain.Math.Polynomial.Exercises as Math+import qualified Domain.Math.Polynomial.IneqExercises as Math+import qualified Domain.Math.Polynomial.RationalExercises as Math+import qualified Domain.Math.Polynomial.Tests as MathPoly+import qualified Domain.Math.Power.Exercises as Math+import qualified Domain.Math.Power.Equation.Exercises as Math+import qualified Domain.Math.SquareRoot.Tests as MathSqrt+import qualified Domain.Math.Polynomial.LeastCommonMultiple as MathLCM+-- import qualified Domain.RegularExpr.Exercises as RE+import qualified Domain.RelationAlgebra as RA++useIDEAS :: DomainReasoner a -> IO a+useIDEAS action = runDomainReasoner $ do+ -- version information+ setVersion shortVersion+ setFullVersion fullVersion+ -- exercise packages+ addPackages packages+ -- services+ addServices serviceList+ addPkgService exerciselistS+ -- domain checks+ addTestSuite $ do+ MathNum.main+ MathPoly.tests+ MathSqrt.tests+ MathInterval.testMe+ MathLCM.testLCM+ LA.checks+ -- do the rest+ action++packages :: [Some ExercisePackage]+packages =+ [ -- logic and relation-algebra+ Some (package Logic.dnfExercise)+ { withOpenMath = True+ , toOpenMath = termToOMOBJ . toTerm . fmap (Math.Var . fromShowString)+ , fromOpenMath = (>>= fromTerm) . omobjToTerm+ , getExerciseText = Just logicText+ }+ , Some (package Logic.dnfUnicodeExercise)+ { withOpenMath = True+ , toOpenMath = termToOMOBJ . toTerm . fmap (Math.Var . fromShowString)+ , fromOpenMath = (>>= fromTerm) . omobjToTerm+ , getExerciseText = Just logicText+ }+ -- , somePackage Logic.proofExercise+ , somePackage RA.cnfExercise+ -- basic math+ -- , someTermPackage Math.naturalExercise+ -- , someTermPackage Math.integerExercise+ -- , someTermPackage Math.rationalExercise+ , someTermPackage Math.fractionExercise+ , someTermPackage Math.coverUpExercise+ , someTermPackage Math.linearExercise+ , someTermPackage Math.linearMixedExercise+ , someTermPackage Math.quadraticExercise+ , someTermPackage Math.higherDegreeExercise+ , someTermPackage Math.findFactorsExercise+ , someTermPackage Math.ineqLinearExercise+ , someTermPackage Math.ineqQuadraticExercise+ , someTermPackage Math.ineqHigherDegreeExercise+ , someTermPackage Math.rationalEquationExercise+ , someTermPackage Math.simplifyRationalExercise+ -- , someTermPackage Math.divisionBrokenExercise+ , someTermPackage Math.quadraticNoABCExercise+ , someTermPackage Math.quadraticWithApproximation+ , someTermPackage Math.derivativeExercise+ , someTermPackage Math.derivativePolyExercise+ , someTermPackage Math.derivativeProductExercise+ , someTermPackage Math.derivativeQuotientExercise+ -- , someTermPackage Math.derivativePowerExercise+ , someTermPackage Math.simplifyPowerExercise+ , someTermPackage Math.powerOfExercise + , someTermPackage Math.nonNegBrokenExpExercise+ , someTermPackage Math.calcPowerExercise+ , someTermPackage Math.powerEqExercise+ , someTermPackage Math.expEqExercise+ , someTermPackage Math.logEqExercise+ -- linear algebra+ , someTermPackage LA.gramSchmidtExercise+ , someTermPackage LA.linearSystemExercise+ , someTermPackage LA.gaussianElimExercise+ , someTermPackage LA.systemWithMatrixExercise+ -- regular expressions+ -- , somePackage RE.regexpExercise+ ]+ +logicText :: ExerciseText Logic.SLogic+logicText = ExerciseText+ { ruleText = Logic.ruleText+ , appliedRule = Logic.appliedRule+ , feedbackSyntaxError = Logic.feedbackSyntaxError+ , feedbackSame = Logic.feedbackSame+ , feedbackBuggy = Logic.feedbackBuggy+ , feedbackNotEquivalent = Logic.feedbackNotEquivalent+ , feedbackOk = Logic.feedbackOk+ , feedbackDetour = Logic.feedbackDetour+ , feedbackUnknown = Logic.feedbackUnknown+ }
src/Main/LoggingDatabase.hs view
@@ -41,7 +41,7 @@ -- insert data into database run conn "INSERT INTO log VALUES (?,?,?,?,?,?,?,?,?,?)" [ toSql $ service req - , toSql $ maybe "unknown" show (exerciseID req) + , toSql $ maybe "unknown" show (exerciseId req) , toSql $ fromMaybe "unknown" (source req) , toSql $ show (dataformat req) , toSql $ maybe "unknown" show (encoding req) @@ -60,7 +60,7 @@ {- -- | Log table schema createStmt = "CREATE TABLE log ( service VARCHAR(250)" - ++ ", exerciseID VARCHAR(250)" + ++ ", exerciseId VARCHAR(250)" ++ ", source VARCHAR(250)" ++ ", dataformat VARCHAR(250)" ++ ", encoding VARCHAR(250)"
src/Main/Options.hs view
@@ -13,7 +13,6 @@ ----------------------------------------------------------------------------- module Main.Options where -import Data.Maybe import Documentation.Make import Main.LoggingDatabase (logEnabled) import Main.Revision @@ -22,7 +21,7 @@ import System.Exit data Flag = Version | Help | Logging Bool | InputFile String - | FixRNG | DocItem DocItem + | FixRNG | DocItem DocItem | DocDir String | TestDir String deriving Eq header :: String @@ -47,19 +46,21 @@ options :: [OptDescr Flag] options = - [ Option [] ["version"] (NoArg Version) "show version number" - , Option "?" ["help"] (NoArg Help) "show options" - , Option "l" ["logging"] (NoArg $ Logging True) "enable logging" - , Option [] ["no-logging"] (NoArg $ Logging False) "disable logging (default on local machine)" - , Option "f" ["file"] (ReqArg InputFile "FILE") "use input FILE as request" - , Option "" ["fixed-rng"] (NoArg FixRNG) "use a fixed random-number generator" - , Option "" ["make-pages"] (docItemDescr (Pages . fromMaybe "docs")) "generate pages for exercises and services" - , Option "" ["make-rules"] (docItemDescr (LatexRules . fromMaybe "docs")) "generate latex code for rewrite rules" - , Option "" ["self-check"] (docItemDescr (SelfCheck . fromMaybe "test")) "perform a self-check" + [ Option [] ["version"] (NoArg Version) "show version number" + , Option "?" ["help"] (NoArg Help) "show options" + , Option "l" ["logging"] (NoArg $ Logging True) "enable logging" + , Option [] ["no-logging"] (NoArg $ Logging False) "disable logging (default on local machine)" + , Option "f" ["file"] (ReqArg InputFile "FILE") "use input FILE as request" + , Option "" ["fixed-rng"] (NoArg FixRNG) "use a fixed random-number generator" + , Option "" ["make-pages"] (NoArg $ DocItem Pages) "generate pages for exercises and services" + , Option "" ["self-check"] (NoArg $ DocItem SelfCheck) "perform a self-check" + , Option "" ["test"] (OptArg testArg "DIR") "run tests on directory (default: 'test')" + , Option "" ["docs-dir"] (ReqArg DocDir "DIR") "directory for documentation (default: 'docs')" + , Option "" ["test-dir"] (ReqArg TestDir "DIR") "directory with tests (default: 'test')" ] -docItemDescr :: (Maybe String -> DocItem) -> ArgDescr Flag -docItemDescr f = OptArg (DocItem . f) "DIR" +testArg :: Maybe String -> Flag +testArg = DocItem . BlackBox serviceOptions :: IO [Flag] serviceOptions = do @@ -77,6 +78,16 @@ docItems :: [Flag] -> [DocItem] docItems flags = [ x | DocItem x <- flags ] + +docDir :: [Flag] -> String +docDir flags = case [ d | DocDir d <- flags ] of + d:_ -> d + _ -> "docs" + +testDir :: [Flag] -> String +testDir flags = case [ d | TestDir d <- flags ] of + d:_ -> d + _ -> "test" documentationMode :: [Flag] -> Bool documentationMode = not . null . docItems
src/Main/Revision.hs view
@@ -1,5 +1,8 @@ -- Automatically generated by Makefile. Do not change. module Main.Revision where-version = "0.6"-revision = 3065-lastChanged = "ma, 19 apr 2010"+version :: String+version = "0.7"+revision :: Int+revision = 3724+lastChanged :: String +lastChanged = "do, 23 dec 2010"
+ src/Service/BasicServices.hs view
@@ -0,0 +1,153 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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.BasicServices + ( -- * Basic Services + stepsremaining, findbuggyrules, ready, allfirsts, derivation + , onefirst, applicable, allapplications, apply, generate, generateWith + ) where + +import Common.Library hiding (derivation, applicable, apply) +import Common.Utils (safeHead) +import Data.List +import Data.Maybe +import System.Random (StdGen, newStdGen) +import Control.Monad +import Service.ExercisePackage +import Service.State +import qualified Common.Classes as Apply + +-- result must be in the IO monad to access a standard random number generator +generate :: ExercisePackage a -> Int -> IO (State a) +generate pkg level = do + stdgen <- newStdGen + return (generateWith stdgen pkg level) + +generateWith :: StdGen -> ExercisePackage a -> Int -> State a +generateWith rng pkg level = + emptyState pkg (randomTermWith rng level (exercise pkg)) + +derivation :: Monad m => Maybe StrategyConfiguration -> State a -> m [(Rule (Context a), Context a)] +derivation mcfg state = + case (statePrefix state, mcfg) of + (Nothing, _) -> fail "Prefix is required" + -- configuration is only allowed beforehand: hence, the prefix + -- should be empty (or else, the configuration is ignored). This + -- restriction should probably be relaxed later on. + (Just p, Just cfg) | null (prefixToSteps p) -> + let newStrategy = configure cfg (strategy ex) + newExercise = ex {strategy = newStrategy} + newPackage = pkg {exercise = newExercise} + in rec timeout [] (empyStateContext newPackage (stateContext state)) + _ -> rec timeout [] state + where + pkg = exercisePkg state + ex = exercise pkg + timeout = 50 :: Int + + rec i acc st = + case onefirst st of + Nothing -> return (reverse acc) + Just (r, _, next) + | i <= 0 -> fail msg + | otherwise -> rec (i-1) ((r, stateContext next) : acc) next + where + msg = "Time out after " ++ show timeout ++ " steps. " ++ + concatMap f (reverse acc) + f (r, c) = let s = maybe "???" (prettyPrinter ex) (fromContext c) + in "[" ++ show r ++ "] " ++ s ++ "; " + +-- Note that we have to inspect the last step of the prefix afterwards, because +-- the remaining part of the derivation could consist of minor rules only. +allfirsts :: Monad m => State a -> m [(Rule (Context a), Location, State a)] +allfirsts state = + case statePrefix state of + Nothing -> + fail "Prefix is required" + Just p0 -> + let tree = cutOnStep (stop . lastStepInPrefix) (prefixTree p0 (stateContext state)) + f (r1, _, _) (r2, _, _) = + ruleOrdering (exercise (exercisePkg state)) r1 r2 + in return (sortBy f (mapMaybe make (derivations tree))) + where + stop (Just (RuleStep r)) = isMajorRule r + stop _ = False + + make d = do + prefixEnd <- safeHead (reverse (steps d)) + termEnd <- safeHead (reverse (terms d)) + case lastStepInPrefix prefixEnd of + Just (RuleStep r) | isMajorRule r -> return + ( r + , location termEnd + , makeState (exercisePkg state) (Just prefixEnd) termEnd + ) + _ -> Nothing + +onefirst :: Monad m => State a -> m (Rule (Context a), Location, State a) +onefirst state = do + xs <- allfirsts state + case xs of + hd:_ -> return hd + [] -> fail "No step possible" + +applicable :: Location -> State a -> [Rule (Context a)] +applicable loc state = + let p r = not (isBuggyRule r) && Apply.applicable r (setLocation loc (stateContext state)) + in filter p (ruleset (exercise (exercisePkg state))) + +allapplications :: State a -> [(Rule (Context a), Location, State a)] +allapplications state = xs ++ ys + where + pkg = exercisePkg state + ex = exercise pkg + xs = concat (allfirsts state) + ps = [ (r, loc) | (r, loc, _) <- xs ] + ys = maybe [] f (top (stateContext state)) + + f c = g c ++ concatMap f (allDowns c) + g c = [ (r, location new, makeState pkg Nothing new) + | r <- ruleset ex + , (r, location c) `notElem` ps + , new <- applyAll r c + ] + +-- local helper +setLocation :: Location -> Context a -> Context a +setLocation loc c0 = fromMaybe c0 (navigateTo loc c0) + +-- Two possible scenarios: either I have a prefix and I can return a new one (i.e., still following the +-- strategy), or I return a new term without a prefix. A final scenario is that the rule cannot be applied +-- to the current term at the given location, in which case the request is invalid. +apply :: Monad m => Rule (Context a) -> Location -> State a -> m (State a) +apply r loc state = maybe applyOff applyOn (statePrefix state) + where + applyOn _ = -- scenario 1: on-strategy + maybe applyOff return $ safeHead + [ s1 | (r1, loc1, s1) <- fromMaybe [] $ allfirsts state, showId r == showId r1, loc==loc1 ] + + applyOff = -- scenario 2: off-strategy + case Apply.apply r (setLocation loc (stateContext state)) of + Just new -> return (makeState (exercisePkg state) Nothing new) + Nothing -> fail ("Cannot apply " ++ show r) + +ready :: State a -> Bool +ready state = isReady (exercise (exercisePkg state)) (stateTerm state) + +stepsremaining :: Monad m => State a -> m Int +stepsremaining = liftM length . derivation Nothing + +findbuggyrules :: State a -> a -> [Rule (Context a)] +findbuggyrules state a = + let ex = exercise (exercisePkg state) + buggies = filter isBuggyRule (ruleset ex) + p r = ruleIsRecognized ex r (stateContext state) (inContext ex a) + in filter p buggies
src/Service/Diagnose.hs view
@@ -12,38 +12,47 @@ -- ----------------------------------------------------------------------------- module Service.Diagnose - ( Diagnosis(..), RuleID, diagnose, restartIfNeeded+ ( Diagnosis(..), diagnose, restartIfNeeded+ , diagnosisType, diagnosisTypeSynonym ) where -import Common.Apply-import Common.Context-import Common.Exercise-import Common.Strategy (emptyPrefix)-import Common.Transformation+import Common.Library import Common.Utils (safeHead)+import Data.List (sortBy) import Data.Maybe-import Service.TypedAbstractService+import Service.ExercisePackage+import Service.State+import Service.BasicServices+import Service.Types ---------------------------------------------------------------- -- Result types for diagnose service -type RuleID a = Rule (Context a)- data Diagnosis a- = Buggy (RuleID a)+ = Buggy (Rule (Context a)) | NotEquivalent | Similar Bool (State a)- | Expected Bool (State a) (RuleID a)- | Detour Bool (State a) (RuleID a)+ | Expected Bool (State a) (Rule (Context a))+ | Detour Bool (State a) (Rule (Context a)) | Correct Bool (State a) +instance Show (Diagnosis a) where+ show diagnosis = + case diagnosis of+ Buggy r -> "Buggy rule " ++ show (show r)+ NotEquivalent -> "Unknown mistake" + Similar _ _ -> "Very similar"+ Expected _ _ r -> "Rule " ++ show (show r) ++ ", expected by strategy"+ Detour _ _ r -> "Rule " ++ show (show r) ++ ", not following strategy"+ Correct _ _ -> "Unknown step"+ ---------------------------------------------------------------- -- The diagnose service diagnose :: State a -> a -> Diagnosis a diagnose state new -- Is the submitted term equivalent?- | not (equivalence ex (term state) new) =+ | not (equivalenceContext ex (stateContext state) newc) = -- Is the rule used discoverable by trying all known buggy rules? case discovered True of Just r -> -- report the buggy rule@@ -52,7 +61,7 @@ NotEquivalent -- Is the submitted term (very) similar to the previous one? - | similarity ex (term state) new =+ | similarity ex (stateTerm state) new = -- If yes, report this Similar (ready state) state @@ -64,35 +73,34 @@ -- Is the rule used discoverable by trying all known rules? | otherwise =- let ns = restartIfNeeded (state { prefix=Nothing, context=inContext ex new })+ let ns = restartIfNeeded (makeState pkg Nothing newc) in case discovered False of Just r -> -- If yes, report the found rule as a detour Detour (ready ns) ns r Nothing -> -- If not, we give up Correct (ready ns) ns where- ex = exercise state+ pkg = exercisePkg state+ ex = exercise pkg+ newc = inContext ex new expected = do xs <- allfirsts (restartIfNeeded state)- let p (_, _, ns) = similarity ex new (term ns)+ let p (_, _, ns) = similarity ex new (stateTerm ns) safeHead (filter p xs) discovered searchForBuggy = safeHead [ r- | r <- ruleset ex+ | r <- sortBy (ruleOrdering ex) (ruleset ex) , isBuggyRule r == searchForBuggy- , ca <- applyAll r (inContext ex sub1)- -- , let s = prettyPrinter (exercise state) (fromContext a)- --, if s=="2*x+2 == 5" then True else error s- , a <- fromContext ca- , similarity ex sub2 a+ , ruleIsRecognized ex r sub1 sub2 ] where - mode = not searchForBuggy- diff = difference ex mode (term state) new- (sub1, sub2) = fromMaybe (term state, new) diff- + (sub1, sub2) = + case difference ex (not searchForBuggy) (stateTerm state) new of + Just (a, b) -> (inContext ex a, inContext ex b) + Nothing -> (stateContext state, newc)+ ---------------------------------------------------------------- -- Helpers @@ -100,14 +108,36 @@ -- Make sure that the new state has a prefix -- When resetting the prefix, also make sure that the context is refreshed restartIfNeeded :: State a -> State a-restartIfNeeded s - | isNothing (prefix s) && canBeRestarted ex = - case fromContext (context s) of - Just a -> s- { prefix = Just (emptyPrefix (strategy ex))- , context = inContext ex a- } - Nothing -> s- | otherwise = s+restartIfNeeded state + | isNothing (statePrefix state) && canBeRestarted (exercise pkg) = + emptyState pkg (stateTerm state)+ | otherwise = state where- ex = exercise s+ pkg = exercisePkg state+ +diagnosisType :: Type a (Diagnosis a)+diagnosisType = useSynonym diagnosisTypeSynonym++diagnosisTypeSynonym :: TypeSynonym a (Diagnosis a)+diagnosisTypeSynonym = typeSynonym "Diagnosis" to from tp+ where+ to (Left r) = Buggy r+ to (Right (Left ())) = NotEquivalent+ to (Right (Right (Left (b, s)))) = Similar b s+ to (Right (Right (Right (Left (b, s, r))))) = Expected b s r+ to (Right (Right (Right (Right (Left (b, s, r)))))) = Detour b s r+ to (Right (Right (Right (Right (Right (b, s)))))) = Correct b s+ + from (Buggy r) = Left r+ from (NotEquivalent) = Right (Left ())+ from (Similar b s) = Right (Right (Left (b, s)))+ from (Expected b s r) = Right (Right (Right (Left (b, s, r))))+ from (Detour b s r) = Right (Right (Right (Right (Left (b, s, r)))))+ from (Correct b s) = Right (Right (Right (Right (Right (b, s)))))+ + tp = Rule+ :|: Unit+ :|: Pair Bool stateTp+ :|: tuple3 Bool stateTp Rule+ :|: tuple3 Bool stateTp Rule+ :|: Pair Bool stateTp
src/Service/DomainReasoner.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XMultiParamTypeClasses -XTypeSynonymInstances #-}+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -12,23 +12,25 @@ ----------------------------------------------------------------------------- module Service.DomainReasoner ( -- * Domain Reasoner data type- DomainReasoner, runDomainReasoner- , liftEither, liftIO, catchError + DomainReasoner, runDomainReasoner, runWithCurrent+ , liftEither, MonadIO(..), catchError -- * Update functions , addPackages, addPackage, addPkgService- , addServices, addService+ , addServices, addService, addTestSuite , setVersion, setFullVersion -- * Accessor functions , getPackages, getExercises, getServices- , getVersion, getFullVersion+ , getVersion, getFullVersion, getTestSuite , findPackage, findService ) where -import Common.Exercise+import Common.Library+import Common.TestSuite import Common.Utils (Some(..)) import Control.Monad.Error import Control.Monad.State-import Service.ServiceList+import Data.Maybe+import Service.Types import Service.ExercisePackage -----------------------------------------------------------------------@@ -39,12 +41,13 @@ data Content = Content { packages :: [Some ExercisePackage] , services :: [Some ExercisePackage] -> [Service]+ , testSuite :: TestSuite , version :: String , fullVersion :: Maybe String } noContent :: Content-noContent = Content [] (const []) [] Nothing+noContent = Content [] (const []) (return ()) [] Nothing runDomainReasoner :: DomainReasoner a -> IO a runDomainReasoner m = do@@ -53,6 +56,11 @@ Left msg -> fail msg Right a -> return a +-- | Returns a run function, based on the current state, inside the monad+runWithCurrent :: DomainReasoner (DomainReasoner a -> IO a)+runWithCurrent =+ get >>= \st -> return (runDomainReasoner . (put st >>))+ liftEither :: Either String a -> DomainReasoner a liftEither = either fail return @@ -68,6 +76,10 @@ throwError = fail catchError m f = DR (unDR m `catchError` (unDR . f)) +instance MonadPlus DomainReasoner where+ mzero = DR mzero+ a `mplus` b = DR (unDR a `mplus` unDR b)+ instance MonadState Content DomainReasoner where get = DR get put s = DR (put s)@@ -89,11 +101,14 @@ c { services = \xs -> f xs : services c xs } addServices :: [Service] -> DomainReasoner ()-addServices = mapM_ addPkgService . map const+addServices = mapM_ (addPkgService . const) addService :: Service -> DomainReasoner () addService s = addServices [s] +addTestSuite :: TestSuite -> DomainReasoner ()+addTestSuite m = modify $ \c -> c { testSuite = testSuite c >> m }+ setVersion :: String -> DomainReasoner () setVersion s = modify $ \c -> c { version = s } @@ -118,18 +133,60 @@ getFullVersion :: DomainReasoner String getFullVersion = gets fullVersion >>= maybe getVersion return -findPackage :: ExerciseCode -> DomainReasoner (Some ExercisePackage)-findPackage code = do+getTestSuite :: DomainReasoner TestSuite+getTestSuite = gets testSuite++findPackage :: Id -> DomainReasoner (Some ExercisePackage)+findPackage i = do pkgs <- getPackages - let p (Some pkg) = exerciseCode (exercise pkg) == code- case filter p pkgs of+ case [ a | a@(Some pkg) <- pkgs, getId pkg == resolveId i ] of [this] -> return this- _ -> fail $ "Package " ++ show code ++ " not found"+ _ -> fail $ "Package " ++ show i ++ " not found" findService :: String -> DomainReasoner Service findService txt = do srvs <- getServices- case filter ((==txt) . serviceName) srvs of+ case filter ((==txt) . showId) srvs of [hd] -> return hd [] -> fail $ "No service " ++ txt _ -> fail $ "Ambiguous service " ++ txt++-----------------------------------------------------------------------+-- Identifier aliases (temporary)++resolveId :: Id -> Id+resolveId i = fromMaybe i (lookup i table)+ where+ table = map (newId *** newId)+ [ ("math.coverup", "algebra.equations.coverup")+ , ("math.lineq", "algebra.equations.linear")+ , ("math.lineq-mixed", "algebra.equations.linear.mixed")+ , ("math.quadreq", "algebra.equations.quadratic") + , ("math.quadreq-no-abc", "algebra.equations.quadratic.no-abc") + , ("math.quadreq-with-approx", "algebra.equations.quadratic.approximate")+ , ("math.higherdegree", "algebra.equations.polynomial")+ , ("math.rationaleq", "algebra.equations.rational")+ , ("math.linineq", "algebra.inequalities.linear")+ , ("math.quadrineq", "algebra.inequalities.quadratic")+ , ("math.ineqhigherdegree", "algebra.inequalities.polynomial")+ , ("math.factor", "algebra.manipulation.polynomial.factor")+ , ("math.simplifyrational", "algebra.manipulation.rational.simplify")+ , ("math.simplifypower", "algebra.manipulation.exponents.simplify")+ , ("math.nonnegexp", "algebra.manipulation.exponents.nonnegative")+ , ("math.powerof", "algebra.manipulation.exponents.powerof")+ , ("math.derivative", "calculus.differentiation")+ , ("math.fraction", "arithmetic.fractions")+ , ("math.calcpower", "arithmetic.exponents")+ , ("linalg.gaussianelim", "linearalgebra.gaussianelim")+ , ("linalg.gramschmidt", "linearalgebra.gramschmidt")+ , ("linalg.linsystem", "linearalgebra.linsystem")+ , ("linalg.systemwithmatrix", "linearalgebra.systemwithmatrix")+ , ("logic.dnf", "logic.propositional.dnf")+ , ("logic.dnf-unicode", "logic.propositional.dnf.unicode")+ , ("relationalg.cnf", "relationalgebra.cnf")+ -- MathDox compatibility+ , ("gaussianelimination" , "linearalgebra.gaussianelim")+ , ("gramschmidt" , "linearalgebra.gramschmidt")+ , ("solvelinearsystem" , "linearalgebra.linsystem")+ , ("solvelinearsystemwithmatrix", "linearalgebra.systemwithmatrix")+ ]
+ src/Service/Evaluator.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE GADTs, Rank2Types #-}+-----------------------------------------------------------------------------+-- Copyright 2010, 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.Evaluator where++import Common.Library+import Control.Monad+import Data.List+import Service.ExercisePackage+import Service.Types+import Service.DomainReasoner++evalService :: Evaluator inp out a -> Service -> inp -> DomainReasoner out+evalService f = eval f . serviceFunction++data Evaluator inp out a = Evaluator + { encoder :: Encoder out a+ , decoder :: Decoder inp a+ }++data Encoder s a = Encoder + { encodeType :: forall t . Type a t -> t -> DomainReasoner s+ , encodeTerm :: a -> DomainReasoner s+ , encodeTuple :: [s] -> s+ }++data Decoder s a = Decoder + { decodeType :: forall t . Type a t -> s -> DomainReasoner (t, s)+ , decodeTerm :: s -> DomainReasoner a+ , decoderPackage :: ExercisePackage a+ } ++decoderExercise :: Decoder s a -> Exercise a+decoderExercise = exercise . decoderPackage++eval :: Evaluator inp out a -> TypedValue a -> inp -> DomainReasoner out+eval f (tv ::: tp) s = + case tp of + t1 :-> t2 -> do+ (a, s1) <- decodeType (decoder f) t1 s+ eval f (tv a ::: t2) s1+ _ ->+ encodeType (encoder f) tp tv++decodeDefault :: Decoder s a -> Type a t -> s -> DomainReasoner (t, s)+decodeDefault dec tp s =+ case tp of+ Iso f _ t -> liftM (first f) (decodeType dec t s)+ Pair t1 t2 -> do+ (a, s1) <- decodeType dec t1 s+ (b, s2) <- decodeType dec t2 s1+ return ((a, b), s2)+ t1 :|: t2 ->+ liftM (first Left) (decodeType dec t1 s) `mplus`+ liftM (first Right) (decodeType dec t2 s)+ Unit -> + return ((), s)+ Tag _ t1 ->+ decodeType dec t1 s+ ExercisePkg ->+ return (decoderPackage dec, s)+ _ ->+ fail $ "No support for argument type: " ++ show tp++encodeDefault :: Encoder s a -> Type a t -> t -> DomainReasoner s+encodeDefault enc tp tv =+ case tp of+ Iso _ f t -> encodeType enc t (f tv)+ Pair t1 t2 -> do+ let (a, b) = tv+ x <- encodeType enc t1 a+ y <- encodeType enc t2 b+ return (encodeTuple enc [x, y])+ t1 :|: t2 -> case tv of+ Left a -> encodeType enc t1 a+ Right b -> encodeType enc t2 b+ Unit -> return (encodeTuple enc [])+ Tag _ t1 -> encodeType enc t1 tv+ IO t1 -> do let pp s | "user error (" `isPrefixOf` s = init (drop 12 s)+ | otherwise = s+ result <- liftIO $ + liftM Right tv `catch` (return . Left . pp . show)+ case result of + Left msg -> fail msg+ Right a -> encodeType enc t1 a+ Rule -> encodeType enc String (showId tv)+ Term -> encodeTerm enc tv+ Context -> fromContext tv >>= encodeType enc Term+ Location -> encodeType enc String (show tv)+ ExercisePkg -> return (encodeTuple enc [])+ _ -> fail ("No support for result type: " ++ show tp)
src/Service/ExercisePackage.hs view
@@ -18,17 +18,18 @@ , package, termPackage, somePackage, someTermPackage -- Conversion functions to/from OpenMath , termToOMOBJ, omobjToTerm+ -- ExerciseText datatype+ , ExerciseText(..) ) where +import Common.Library import Common.Utils (Some(..))-import Common.Exercise-import Control.Monad import Common.Rewriting.Term+import Control.Monad import Data.Char import Data.List-import Service.FeedbackText (ExerciseText) import Text.OpenMath.Object-import Text.OpenMath.Symbol+import qualified Text.OpenMath.Symbol as OM import Text.OpenMath.Dictionary.Fns1 -----------------------------------------------------------------------------@@ -42,6 +43,10 @@ , getExerciseText :: Maybe (ExerciseText a) } +instance HasId (ExercisePackage a) where+ getId = getId . exercise+ changeId f pkg = pkg { exercise = changeId f (exercise pkg) }+ package :: Exercise a -> ExercisePackage a package ex = P { exercise = ex@@ -63,22 +68,20 @@ someTermPackage :: IsTerm a => Exercise a -> Some ExercisePackage someTermPackage = Some . termPackage-+ ----------------------------------------------------------------------------- -- Utility functions for conversion to/from OpenMath termToOMOBJ :: Term -> OMOBJ termToOMOBJ term = case term of- Var s -> OMV s- Con s -> case s of- S (Just a) b -> OMS (makeSymbol a b)- S Nothing b -> OMS (extraSymbol b)- Meta i -> OMV ("$" ++ show i)- Num n -> OMI n- Float d -> OMF d- App _ _ -> let (f, xs) = getSpine term- in make (map termToOMOBJ (f:xs))+ Var s -> OMV s+ Con s -> OMS (idToSymbol (getId s))+ Meta i -> OMV ('$' : show i)+ Num n -> OMI n+ Float d -> OMF d+ Apply _ _ -> let (f, xs) = getSpine term+ in make (map termToOMOBJ (f:xs)) where make [OMS s, OMV x, body] | s == lambdaSymbol = OMBIND (OMS s) [x] body@@ -90,7 +93,7 @@ OMV x -> case isMeta x of Just n -> return (Meta n) Nothing -> return (Var x)- OMS s -> return (Con (S (dictionary s) (symbolName s)))+ OMS s -> return (symbol (newSymbol (OM.dictionary s # OM.symbolName s))) OMI n -> return (Num n) OMF a -> return (Float a) OMA (x:xs) -> liftM2 makeTerm (omobjToTerm x) (mapM omobjToTerm xs)@@ -100,3 +103,27 @@ where isMeta ('$':xs) = Just (foldl' (\a b -> a*10+ord b-48) 0 xs) -- ' isMeta _ = Nothing++idToSymbol :: Id -> OM.Symbol+idToSymbol a+ | null (qualifiers a) = + OM.extraSymbol (unqualified a)+ | otherwise = + OM.makeSymbol (qualification a) (unqualified a)++------------------------------------------------------------+-- Exercise Text data type+-- Note: ideally, this should be defined elsewhere++-- Exercise extension for textual feedback+data ExerciseText a = ExerciseText+ { ruleText :: Rule (Context a) -> Maybe String+ , appliedRule :: Rule (Context a) -> String+ , feedbackSyntaxError :: String -> String+ , feedbackSame :: String+ , feedbackBuggy :: Bool -> [Rule (Context a)] -> String+ , feedbackNotEquivalent :: Bool -> String+ , feedbackOk :: [Rule (Context a)] -> (String, Bool)+ , feedbackDetour :: Bool -> Maybe (Rule (Context a)) -> [Rule (Context a)] -> (String, Bool)+ , feedbackUnknown :: Bool -> String+ }
src/Service/FeedbackText.hs view
@@ -14,61 +14,49 @@ , onefirsttext, submittext, derivationtext, submitHelper ) where -import Control.Arrow-import Common.Context-import Common.Exercise-import Common.Transformation-import Common.Utils+import Common.Library hiding (derivation) import Data.Maybe+import Common.Utils import Service.Diagnose (restartIfNeeded)+import Service.ExercisePackage+import Service.State import Service.Submit-import Service.TypedAbstractService----------------------------------------------------------------- Exercise Text data type---- Exercise extension for textual feedback-data ExerciseText a = ExerciseText- { ruleText :: Rule (Context a) -> Maybe String- , appliedRule :: Rule (Context a) -> String- , feedbackSyntaxError :: String -> String- , feedbackSame :: String- , feedbackBuggy :: Bool -> [Rule (Context a)] -> String- , feedbackNotEquivalent :: Bool -> String- , feedbackOk :: [Rule (Context a)] -> (String, Bool)- , feedbackDetour :: Bool -> Maybe (Rule (Context a)) -> [Rule (Context a)] -> (String, Bool)- , feedbackUnknown :: Bool -> String- }-+import Service.BasicServices+ ------------------------------------------------------------ -- Services -derivationtext :: Monad m => ExerciseText a -> State a -> Maybe String -> m [(String, Context a)]-derivationtext exText st _event = do- xs <- derivation Nothing st+derivationtext :: Monad m => State a -> Maybe String -> m [(String, Context a)]+derivationtext state _event = do+ exText <- exerciseText state+ xs <- derivation Nothing state return (map (first (showRule exText)) xs) -onefirsttext :: ExerciseText a -> State a -> Maybe String -> (Bool, String, State a)-onefirsttext exText state event =- case allfirsts state of- Just ((r, _, s):_) ->- let msg = case fromContext (context s) >>= useToRewrite exText r state of- Just txt | event /= Just "hint button" -> txt- _ -> "Use " ++ showRule exText r- in (True, msg, s)- _ -> (False, "Sorry, no hint available", state)+onefirsttext :: Monad m => State a -> Maybe String -> m (Bool, String, State a)+onefirsttext state event =+ case onefirst state of+ Just (r, _, s) -> do+ exText <- exerciseText state+ let mtxt = fromContext (stateContext s) >>= useToRewrite exText r state+ msg = case mtxt of+ Just txt | event /= Just "hint button" -> txt+ _ -> "Use " ++ showRule exText r+ return (True, msg, s)+ _ -> return (False, "Sorry, no hint available", state) -submittext :: ExerciseText a -> State a -> String -> Maybe String -> (Bool, String, State a)-submittext exText state txt _event = - case parser (exercise state) txt of- Left err -> - (False, feedbackSyntaxError exText err, state)- Right a -> - let result = submit state a- (txt, b) = submitHelper exText state a result- in case getResultState result of- Just new | b -> (True, txt, restartIfNeeded new)- _ -> (False, txt, state)+submittext :: Monad m => State a -> String -> Maybe String -> m (Bool, String, State a)+submittext state input _event = do+ exText <- exerciseText state+ return $+ case parser (exercise (exercisePkg state)) input of+ Left err -> + (False, feedbackSyntaxError exText err, state)+ Right a -> + let result = submit state a+ (txt, b) = submitHelper exText state a result+ in case getResultState result of+ Just new | b -> (True, txt, restartIfNeeded new)+ _ -> (False, txt, state) -- Feedback messages for submit service (free student input). The boolean -- indicates whether the student is allowed to continue (True), or forced @@ -99,23 +87,25 @@ showRule :: ExerciseText a -> Rule (Context a) -> String showRule exText r = - case ruleText exText r of- Just s -> s- Nothing -> "rule " ++ name r+ fromMaybe ("rule " ++ showId r) (ruleText exText r) useToRewrite :: ExerciseText a -> Rule (Context a) -> State a -> a -> Maybe String-useToRewrite exText rule old = rewriteIntoText True txt old+useToRewrite exText r old = rewriteIntoText True txt old where- txt = "Use " ++ showRule exText rule- ++ " to rewrite "+ txt = "Use " ++ showRule exText r ++ " to rewrite " youRewroteInto :: State a -> a -> Maybe String youRewroteInto = rewriteIntoText False "You rewrote " rewriteIntoText :: Bool -> String -> State a -> a -> Maybe String rewriteIntoText mode txt old a = do- let ex = exercise old- p <- fromContext (context old)+ let ex = exercise (exercisePkg old)+ p <- fromContext (stateContext old) (p1, a1) <- difference ex mode p a return $ txt ++ prettyPrinter ex p1 ++ " into " ++ prettyPrinter ex a1 ++ ". "++exerciseText :: Monad m => State a -> m (ExerciseText a)+exerciseText = + let msg = "No support for textual feedback"+ in maybe (fail msg) return . getExerciseText . exercisePkg
src/Service/ModeJSON.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XGADTs #-} +{-# LANGUAGE GADTs #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -14,18 +14,15 @@ ----------------------------------------------------------------------------- module Service.ModeJSON (processJSON, jsonTuple) where -import Common.Context +import Common.Library hiding (exerciseId) import Common.Utils (Some(..), distinct, readM) -import Common.Exercise -import Common.Strategy (makePrefix) -import Common.Transformation hiding (ruleList, defaultArgument) import Text.JSON import Service.Request -import Service.Types (TypedValue(..), Evaluator(..), Type, encodeDefault, decodeDefault, Encoder(..), Decoder(..), decoderExercise) +import Service.State import qualified Service.Types as Tp -import qualified Service.TypedAbstractService as TAS +import Service.Types hiding (String) import Service.Submit -import Service.ServiceList hiding (Service) +import Service.Evaluator import Service.ExercisePackage import Service.DomainReasoner import Control.Monad @@ -33,14 +30,16 @@ import Data.Char -- TODO: Clean-up code -extractCode :: JSON -> ExerciseCode -extractCode = fromMaybe noCode . readCode . f +extractExerciseId :: Monad m => JSON -> m Id +extractExerciseId json = + case json of + String s -> return (newId s) + Array [String _, String _, a@(Array _)] -> extractExerciseId a + Array (String s:tl) | any p s -> extractExerciseId (Array tl) + Array (hd:_) -> extractExerciseId hd + _ -> fail "no code" where - f (String s) = s - f (Array [String _, String _, a@(Array _)]) = f a - f (Array (String s:tl)) | any (\c -> not (isAlphaNum c || isSpace c || c `elem` ".-")) s = f (Array tl) - f (Array (hd:_)) = f hd - f _ = "" + p c = not (isAlphaNum c || isSpace c || c `elem` ".-") processJSON :: String -> DomainReasoner (Request, String, String) processJSON input = do @@ -64,7 +63,7 @@ srv <- case lookupM "method" json of Just (String s) -> return s _ -> fail "Invalid method" - code <- liftM (return . extractCode) (lookupM "params" json) + let a = (lookupM "params" json >>= extractExerciseId) enc <- case lookupM "encoding" json of Nothing -> return Nothing Just (String s) -> liftM Just (readEncoding s) @@ -75,7 +74,7 @@ _ -> fail "Invalid source" return Request { service = srv - , exerciseID = code + , exerciseId = a , source = src , dataformat = JSON , encoding = enc @@ -83,38 +82,45 @@ myHandler :: JSON_RPC_Handler DomainReasoner myHandler fun arg = do - pkg <- if fun == "exerciselist" - then return (Some (package emptyExercise)) - else findPackage (extractCode arg) - srv <- findService fun + pkg <- if fun == "exerciselist" + then return (Some (package emptyExercise)) + else extractExerciseId arg >>= findPackage + srv <- findService fun case jsonConverter pkg of - Some conv -> do - either fail return (evalService conv srv arg) + Some conv -> + evalService conv srv arg -jsonConverter :: Some ExercisePackage -> Some (Evaluator (Either String) JSON JSON) +jsonConverter :: Some ExercisePackage -> Some (Evaluator JSON JSON) jsonConverter (Some pkg) = Some (Evaluator (jsonEncoder (exercise pkg)) (jsonDecoder pkg)) -jsonEncoder :: Monad m => Exercise a -> Encoder m JSON a +jsonEncoder :: Exercise a -> Encoder JSON a jsonEncoder ex = Encoder { encodeType = encode (jsonEncoder ex) , encodeTerm = return . String . prettyPrinter ex , encodeTuple = jsonTuple } where - encode :: Monad m => Encoder m JSON a -> Type a t -> t -> m JSON + encode :: Encoder JSON a -> Type a t -> t -> DomainReasoner JSON encode enc serviceType a | length xs > 1 = liftM jsonTuple (mapM (\(b ::: t) -> encode enc t b) xs) | otherwise = case serviceType of + Tp.Tag s t | s == "Result" -> do + result <- isSynonym submitTypeSynonym (a ::: serviceType) + encodeResult enc result + | s == "elem" -> + encode enc t a + | s == "State" -> do + st <- isSynonym stateTypeSynonym (a ::: serviceType) + encodeState (encodeTerm enc) st + Tp.List t -> liftM Array (mapM (encode enc t) a) Tp.Tag s t -> liftM (\b -> Object [(s, b)]) (encode enc t a) Tp.Int -> return (toJSON a) Tp.Bool -> return (toJSON a) Tp.String -> return (toJSON a) - Tp.State -> encodeState (encodeTerm enc) a - Tp.Result -> encodeResult enc a _ -> encodeDefault enc serviceType a where xs = tupleList (a ::: serviceType) @@ -124,8 +130,8 @@ tupleList (p ::: Tp.Pair t1 t2) = tupleList (fst p ::: t1) ++ tupleList (snd p ::: t2) tupleList tv = [tv] - -jsonDecoder :: MonadPlus m => ExercisePackage a -> Decoder m JSON a + +jsonDecoder :: ExercisePackage a -> Decoder JSON a jsonDecoder pkg = Decoder { decodeType = decode (jsonDecoder pkg) , decodeTerm = reader (exercise pkg) @@ -136,23 +142,25 @@ reader ex (String s) = either (fail . show) return (parser ex s) reader _ _ = fail "Expecting a string when reading a term" - decode :: MonadPlus m => Decoder m JSON a -> Type a t -> JSON -> m (t, JSON) + decode :: Decoder JSON a -> Type a t -> JSON -> DomainReasoner (t, JSON) decode dec serviceType = case serviceType of - Tp.State -> useFirst $ decodeState (decoderExercise dec) (decodeTerm dec) Tp.Location -> useFirst decodeLocation Tp.Term -> useFirst $ decodeTerm dec - Tp.Rule -> useFirst $ \x -> fromJSON x >>= getRule (decoderExercise dec) - Tp.Exercise -> \json -> case json of - (Array (String _:rest)) -> return (decoderExercise dec, Array rest) - _ -> return (decoderExercise dec, json) + Tp.Rule -> useFirst $ \x -> jsonToId x >>= getRule (decoderExercise dec) + Tp.ExercisePkg -> \json -> case json of + Array (String _:rest) -> return (decoderPackage dec, Array rest) + _ -> return (decoderPackage dec, json) Tp.Int -> useFirst $ \json -> case json of Number (I n) -> return (fromIntegral n) _ -> fail "not an integer" Tp.String -> useFirst $ \json -> case json of String s -> return s _ -> fail "not a string" - _ -> decodeDefault dec serviceType + Tp.Tag s _ | s == "State" -> do + f <- equalM stateTp serviceType + useFirst (liftM f . decodeState (decoderPackage dec) (decodeTerm dec)) + _ -> decodeDefault dec serviceType useFirst :: Monad m => (JSON -> m a) -> JSON -> m (a, JSON) useFirst f (Array (x:xs)) = do @@ -160,20 +168,23 @@ return (a, Array xs) useFirst _ _ = fail "expecting an argument" +jsonToId :: Monad m => JSON -> m Id +jsonToId = liftM (newId :: String -> Id) . fromJSON + decodeLocation :: Monad m => JSON -> m [Int] decodeLocation (String s) = readM s decodeLocation _ = fail "expecting a string for a location" -------------------------- -encodeState :: Monad m => (a -> m JSON) -> TAS.State a -> m JSON +encodeState :: Monad m => (a -> m JSON) -> State a -> m JSON encodeState f st = do - theTerm <- f (TAS.term st) + theTerm <- f (stateTerm st) return $ Array - [ String (show (exerciseCode (TAS.exercise st))) - , String (maybe "NoPrefix" show (TAS.prefix st)) + [ String (showId (exercisePkg st)) + , String (maybe "NoPrefix" show (statePrefix st)) , theTerm - , encodeContext (getEnvironment (TAS.context st)) + , encodeContext (getEnvironment (stateContext st)) ] encodeContext :: Environment -> JSON @@ -181,16 +192,14 @@ where f k = (k, String $ fromMaybe "" $ lookupEnv k env) -decodeState :: Monad m => Exercise a -> (JSON -> m a) -> JSON -> m (TAS.State a) -decodeState ex f (Array [a]) = decodeState ex f a -decodeState ex f (Array [String _code, String p, ce, jsonContext]) = do +decodeState :: Monad m => ExercisePackage a -> (JSON -> m a) -> JSON -> m (State a) +decodeState pkg f (Array [a]) = decodeState pkg f a +decodeState pkg f (Array [String _code, String p, ce, jsonContext]) = do + let ex = exercise pkg + mpr = readM p >>= (`makePrefix` strategy ex) a <- f ce env <- decodeContext jsonContext - return TAS.State - { TAS.exercise = ex - , TAS.prefix = readM p >>= (`makePrefix` strategy ex) - , TAS.context = makeContext ex env a - } + return $ makeState pkg mpr (makeContext ex env a) decodeState _ _ s = fail $ "invalid state" ++ show s decodeContext :: Monad m => JSON -> m Environment @@ -201,26 +210,26 @@ add _ _ = fail "invalid item in context" decodeContext json = fail $ "invalid context: " ++ show json -encodeResult :: Monad m => Encoder m JSON a -> Result a -> m JSON +encodeResult :: Encoder JSON a -> Result a -> DomainReasoner JSON encodeResult enc result = case result of - -- TAS.SyntaxError _ -> [("result", String "SyntaxError")] - Buggy rs -> return $ Object [("result", String "Buggy"), ("rules", Array $ map (String . name) rs)] + -- SyntaxError _ -> [("result", String "SyntaxError")] + Buggy rs -> return $ Object [("result", String "Buggy"), ("rules", Array $ map (String . showId) rs)] NotEquivalent -> return $ Object [("result", String "NotEquivalent")] Ok rs st -> do - json <- encodeType enc Tp.State st - return $ Object [("result", String "Ok"), ("rules", Array $ map (String . name) rs), ("state", json)] + json <- encodeType enc stateTp st + return $ Object [("result", String "Ok"), ("rules", Array $ map (String . showId) rs), ("state", json)] Detour rs st -> do - json <- encodeType enc Tp.State st - return $ Object [("result", String "Detour"), ("rules", Array $ map (String . name) rs), ("state", json)] + json <- encodeType enc stateTp st + return $ Object [("result", String "Detour"), ("rules", Array $ map (String . showId) rs), ("state", json)] Unknown st -> do - json <- encodeType enc Tp.State st + json <- encodeType enc stateTp st return $ Object [("result", String "Unknown"), ("state", json)] jsonTuple :: [JSON] -> JSON jsonTuple xs = case mapM f xs of - Just xs | distinct (map fst xs) -> Object xs + Just ys | distinct (map fst ys) -> Object ys _ -> Array xs where f (Object [p]) = Just p
src/Service/ModeXML.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XGADTs #-} +{-# LANGUAGE GADTs #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -17,10 +17,7 @@ , resultOk, resultError, addVersion ) where -import Common.Navigator -import Common.Context -import Common.Exercise -import Common.Strategy hiding (not, fail) +import Common.Library hiding (exerciseId) import Common.Utils (Some(..), readM) import Control.Monad import Data.Char @@ -30,15 +27,14 @@ import Service.ProblemDecomposition import Service.Request import Service.RulesInfo (rulesInfoXML) -import Service.ServiceList import Service.StrategyInfo -import Service.TypedAbstractService hiding (exercise) +import Service.State import Service.Diagnose -import Service.Types hiding (State) +import Service.Types +import qualified Service.Types as Tp +import Service.Evaluator import Text.OpenMath.Object import Text.XML -import qualified Common.Transformation as Rule -import qualified Service.Types as Tp import Service.DomainReasoner processXML :: String -> DomainReasoner (Request, String, String) @@ -46,7 +42,7 @@ xml <- liftEither (parseXML input) req <- liftEither (xmlRequest xml) resp <- xmlReply req xml - `catchError` \msg -> return (resultError msg) + `catchError` (return . resultError) vers <- getVersion let out = showXML (if null vers then resp else addVersion vers resp) return (req, out, "application/xml") @@ -61,59 +57,38 @@ unless (name xml == "request") $ fail "expected xml tag request" srv <- findAttribute "service" xml - let code = extractExerciseCode xml + let a = extractExerciseId xml enc <- case findAttribute "encoding" xml of Just s -> liftM Just (readEncoding s) Nothing -> return Nothing return Request { service = srv - , exerciseID = code + , exerciseId = a , source = findAttribute "source" xml , dataformat = XML , encoding = enc } xmlReply :: Request -> XML -> DomainReasoner XML -xmlReply request xml - | service request == "mathdox" = do - code <- maybe (fail "unknown exercise code") return (exerciseID request) - Some pkg <- findPackage code - (st, sloc, answer) <- liftEither $ xmlToRequest xml (fromOpenMath pkg) (exercise pkg) - return (replyToXML (toOpenMath pkg) (problemDecomposition st sloc answer)) - xmlReply request xml = do srv <- findService (service request) pkg <- - case exerciseID request of + case exerciseId request of Just code -> findPackage code Nothing | service request == "exerciselist" -> return (Some (package emptyExercise)) | otherwise -> fail "unknown exercise code" - Some conv <- return $ + Some conv <- case encoding request of - Just StringEncoding -> stringFormatConverter pkg - _ -> openMathConverter pkg - res <- liftEither $ evalService conv srv xml + Just StringEncoding -> return (stringFormatConverter pkg) + _ -> return (openMathConverter pkg) + res <- evalService conv srv xml return (resultOk res) -extractExerciseCode :: Monad m => XML -> m ExerciseCode -extractExerciseCode xml = - case liftM (break (== '.')) (findAttribute "exerciseid" xml) of - Just (as, _:bs) -> return (makeCode as bs) - Just (as, _) -> maybe (fail "invalid code") return (readCode as) - -- being backwards compatible with early MathDox - Nothing -> do - let getName = map toLower . filter isAlphaNum . getData - linalg = return . makeCode "linalg" - case fmap getName (findChild "strategy" xml) of - Just name - | name == "gaussianelimination" -> linalg "gaussianelim" - | name == "gramschmidt" -> linalg "gramschmidt" - | name == "solvelinearsystem" -> linalg "linsystem" - | name == "solvelinearsystemwithmatrix" -> linalg "systemwithmatrix" - _ -> fail "no exerciseid attribute, nor a known strategy element" +extractExerciseId :: Monad m => XML -> m Id +extractExerciseId = liftM newId . findAttribute "exerciseid" resultOk :: XMLBuilder -> XML resultOk body = makeXML "reply" $ do @@ -128,91 +103,109 @@ ------------------------------------------------------------ -- Mixing abstract syntax (OpenMath format) and concrete syntax (string) -stringFormatConverter :: Some ExercisePackage -> Some (Evaluator (Either String) XML XMLBuilder) +stringFormatConverter :: Some ExercisePackage -> Some (Evaluator XML XMLBuilder) stringFormatConverter (Some pkg) = Some (stringFormatConverterTp pkg) -stringFormatConverterTp :: ExercisePackage a -> Evaluator (Either String) XML XMLBuilder a +stringFormatConverterTp :: ExercisePackage a -> Evaluator XML XMLBuilder a stringFormatConverterTp pkg = - Evaluator (xmlEncoder False f ex) (xmlDecoder False g pkg) + Evaluator (xmlEncoder False f pkg) (xmlDecoder False g pkg) where ex = exercise pkg f = return . element "expr" . text . prettyPrinter ex - g xml = do - xml <- findChild "expr" xml -- quick fix + g xml0 = do + xml <- findChild "expr" xml0 -- quick fix -- guard (name xml == "expr") let input = getData xml either (fail . show) return (parser ex input) -openMathConverter :: Some ExercisePackage -> Some (Evaluator (Either String) XML XMLBuilder) +openMathConverter :: Some ExercisePackage -> Some (Evaluator XML XMLBuilder) openMathConverter (Some pkg) = Some (openMathConverterTp pkg) -openMathConverterTp :: ExercisePackage a -> Evaluator (Either String) XML XMLBuilder a +openMathConverterTp :: ExercisePackage a -> Evaluator XML XMLBuilder a openMathConverterTp pkg = - Evaluator (xmlEncoder True f ex) (xmlDecoder True g pkg) + Evaluator (xmlEncoder True f pkg) (xmlDecoder True g pkg) where - ex = exercise pkg f = return . builder . toXML . toOpenMath pkg g xml = do xob <- findChild "OMOBJ" xml - omobj <- xml2omobj xob + omobj <- liftEither (xml2omobj xob) case fromOpenMath pkg omobj of Just a -> return a Nothing -> fail "Unknown OpenMath object" -xmlEncoder :: Monad m => Bool -> (a -> m XMLBuilder) -> Exercise a -> Encoder m XMLBuilder a -xmlEncoder b f ex = Encoder - { encodeType = encode (xmlEncoder b f ex) ex +xmlEncoder :: Bool -> (a -> DomainReasoner XMLBuilder) -> ExercisePackage a -> Encoder XMLBuilder a +xmlEncoder b f pkg = Encoder + { encodeType = xmlEncodeType b (xmlEncoder b f pkg) pkg , encodeTerm = f , encodeTuple = sequence_ } - where - encode :: Monad m => Encoder m XMLBuilder a -> Exercise a -> Type a t -> t -> m XMLBuilder - encode enc ex serviceType = - case serviceType of - Tp.List t1 -> \xs -> - case allAreTagged t1 of - Just f -> do - let make = element "elem" . mapM_ (uncurry (.=.)) . f - let elems = mapM_ make xs - return (element "list" elems) - _ -> do - bs <- mapM (encode enc ex t1) xs - let elems = mapM_ (element "elem") bs - return (element "list" elems) - Tp.Elem t1 -> liftM (element "elem") . encode enc ex t1 - Tp.Tag s t1 -> liftM (element s) . encode enc ex t1 -- quick fix - Tp.Strategy -> return . builder . strategyToXML - Tp.Rule -> return . ("ruleid" .=.) . Rule.name - Tp.RulesInfo -> \_ -> rulesInfoXML ex (encodeTerm enc) - Tp.Term -> encodeTerm enc - Tp.Diagnosis -> encodeDiagnosis b (encodeTerm enc) - Tp.Context -> encodeContext b (encodeTerm enc) - Tp.Location -> return . {-element "location" .-} text . show - Tp.Bool -> return . text . map toLower . show - Tp.String -> return . text - Tp.Int -> return . text . show - Tp.State -> encodeState b (encodeTerm enc) - _ -> encodeDefault enc serviceType -xmlDecoder :: MonadPlus m => Bool -> (XML -> m a) -> ExercisePackage a -> Decoder m XML a +xmlEncodeType :: Bool -> Encoder XMLBuilder a -> ExercisePackage a -> Type a t -> t -> DomainReasoner XMLBuilder +xmlEncodeType b enc pkg serviceType = + case serviceType of + Tp.Tag s _ + | s == "Diagnosis" -> \a -> do + d <- isSynonym diagnosisTypeSynonym (a ::: serviceType) + encodeDiagnosis b (encodeTerm enc) d + | s == "DecompositionReply" -> \a -> do + reply <- isSynonym replyTypeSynonym (a ::: serviceType) + encodeReply (encodeState b (encodeTerm enc)) reply + | s == "RulesInfo" -> \_ -> + rulesInfoXML (exercise pkg) (encodeTerm enc) + | s == "State" -> \a -> do + st <- isSynonym stateTypeSynonym (a ::: serviceType) + encodeState b (encodeTerm enc) st + Tp.List t1 -> \xs -> + case allAreTagged t1 of + Just f -> do + let make = element "elem" . mapM_ (uncurry (.=.)) . f + let elems = mapM_ make xs + return (element "list" elems) + _ -> do + bs <- mapM (xmlEncodeType b enc pkg t1) xs + let elems = mapM_ (element "elem") bs + return (element "list" elems) + Tp.Tag s t1 -> liftM (element s) . xmlEncodeType b enc pkg t1 -- quick fix + Tp.Strategy -> return . builder . strategyToXML + Tp.Rule -> return . ("ruleid" .=.) . showId + Tp.Term -> encodeTerm enc + Tp.Context -> encodeContext b (encodeTerm enc) + Tp.Location -> return . {-element "location" .-} text . show + Tp.Id -> return . text . show + Tp.Bool -> return . text . map toLower . show + Tp.String -> return . text + Tp.Int -> return . text . show + _ -> encodeDefault enc serviceType + +xmlDecoder :: Bool -> (XML -> DomainReasoner a) -> ExercisePackage a -> Decoder XML a xmlDecoder b f pkg = Decoder - { decodeType = decode (xmlDecoder b f pkg) + { decodeType = xmlDecodeType b (xmlDecoder b f pkg) , decodeTerm = f , decoderPackage = pkg } - where - decode :: MonadPlus m => Decoder m XML a -> Type a t -> XML -> m (t, XML) - decode dec serviceType = - case serviceType of - Tp.State -> decodeState b (decoderExercise dec) (decodeTerm dec) - Tp.Location -> leave $ liftM (read . getData) . findChild "location" - Tp.Rule -> leave $ fromMaybe (fail "unknown rule") . liftM (getRule (decoderExercise dec) . getData) . findChild "ruleid" - Tp.Term -> \xml -> decodeTerm dec xml >>= \a -> return (a, xml) - Tp.StrategyCfg -> decodeConfiguration - _ -> decodeDefault dec serviceType - - leave :: Monad m => (XML -> m a) -> XML -> m (a, XML) - leave f xml = liftM (\a -> (a, xml)) (f xml) + +xmlDecodeType :: Bool -> Decoder XML a -> Type a t -> XML -> DomainReasoner (t, XML) +xmlDecodeType b dec serviceType = + case serviceType of + Tp.Context -> keep $ decodeContext b (decoderPackage dec) (decodeTerm dec) + Tp.Location -> keep $ liftM (read . getData) . findChild "location" + Tp.Id -> keep $ \xml -> do + a <- findChild "location" xml + return (newId (getData a)) + Tp.Rule -> keep $ fromMaybe (fail "unknown rule") . liftM (getRule (decoderExercise dec) . newId . getData) . findChild "ruleid" + Tp.Term -> keep $ decodeTerm dec + Tp.StrategyCfg -> decodeConfiguration + Tp.Tag s t + | s == "State" -> \xml -> do + g <- equalM stateTp serviceType + st <- decodeState b (decoderPackage dec) (decodeTerm dec) xml + return (g st, xml) + | s == "answer" -> \xml -> + findChild "answer" xml >>= xmlDecodeType b dec t + _ -> decodeDefault dec serviceType + where + keep :: Monad m => (XML -> m a) -> XML -> m (a, XML) + keep f xml = liftM (\a -> (a, xml)) (f xml) allAreTagged :: Type a t -> Maybe (t -> [(String, String)]) allAreTagged (Iso _ f t) = fmap (. f) (allAreTagged t) @@ -220,32 +213,39 @@ f1 <- allAreTagged t1 f2 <- allAreTagged t2 return $ \(a,b) -> f1 a ++ f2 b -allAreTagged (Tag tag Bool) = Just $ \b -> [(tag, map toLower (show b))] -allAreTagged (Tag tag String) = Just $ \s -> [(tag, s)] +allAreTagged (Tag s Bool) = Just $ \b -> [(s, map toLower (show b))] +allAreTagged (Tag s String) = Just $ \a -> [(s, a)] allAreTagged _ = Nothing -decodeState :: Monad m => Bool -> Exercise a -> (XML -> m a) -> XML -> m (State a, XML) -decodeState b ex f top = do - xml <- findChild "state" top +decodeState :: Monad m => Bool -> ExercisePackage a -> (XML -> m a) -> XML -> m (State a) +decodeState b pkg f xmlTop = do + xml <- findChild "state" xmlTop unless (name xml == "state") (fail "expected a state tag") - mpr <- case maybe "" getData (findChild "prefix" xml) of - prefixText - | all isSpace prefixText -> - return (Just (emptyPrefix (strategy ex))) - | prefixText ~= "no prefix" -> - return Nothing - | otherwise -> do - a <- readM prefixText - pr <- makePrefix a (strategy ex) - return (Just pr) - expr <- f xml - env <- decodeEnvironment b xml - let state = State ex mpr term - term = makeContext ex env expr - return (state, top) + mpr <- decodePrefix pkg xml + term <- decodeContext b pkg f xml + return (makeState pkg mpr term) + +decodePrefix :: Monad m => ExercisePackage a -> XML -> m (Maybe (Prefix (Context a))) +decodePrefix pkg xml + | all isSpace prefixText = + return (Just (emptyPrefix str)) + | prefixText ~= "no prefix" = + return Nothing + | otherwise = do + a <- readM prefixText + pr <- makePrefix a str + return (Just pr) where + prefixText = maybe "" getData (findChild "prefix" xml) + str = strategy (exercise pkg) a ~= b = g a == g b g = map toLower . filter (not . isSpace) + +decodeContext :: Monad m => Bool -> ExercisePackage a -> (XML -> m a) -> XML -> m (Context a) +decodeContext b pkg f xml = do + expr <- f xml + env <- decodeEnvironment b xml + return (makeContext (exercise pkg) env expr) decodeEnvironment :: Monad m => Bool -> XML -> m Environment decodeEnvironment b xml = @@ -256,18 +256,18 @@ add env item = do unless (name item == "item") $ fail $ "expecting item tag, found " ++ name item - name <- findAttribute "name" item + n <- findAttribute "name" item case findChild "OMOBJ" item of -- OpenMath object found inside item tag - Just this | b -> do + Just this | b -> case xml2omobj this >>= omobjToTerm of Left err -> fail err Right term -> - return (storeEnv name term env) + return (storeEnv n term env) -- Simple value in attribute _ -> do value <- findAttribute "value" item - return (storeEnv name value env) + return (storeEnv n value env) decodeConfiguration :: MonadPlus m => XML -> m (StrategyConfiguration, XML) decodeConfiguration xml = @@ -282,22 +282,25 @@ Just a -> return a Nothing -> fail $ "unknown action " ++ show (name item) cfgloc <- findAttribute "name" item - return $ (ByName cfgloc, action) - + return (byName (newId cfgloc), action) encodeState :: Monad m => Bool -> (a -> m XMLBuilder) -> State a -> m XMLBuilder -encodeState b f state = f (term state) >>= \body -> return $ - element "state" $ do - element "prefix" (text $ maybe "no prefix" show (prefix state)) - let env = getEnvironment (context state) - encodeEnvironment b (location (context state)) env +encodeState b f state = do + body <- f (stateTerm state) + return $ element "state" $ do + encodePrefix (statePrefix state) + let env = getEnvironment (stateContext state) + encodeEnvironment b (location (stateContext state)) env body +encodePrefix :: Maybe (Prefix a) -> XMLBuilder +encodePrefix = element "prefix" . text . maybe "no prefix" show + encodeEnvironment :: Bool -> Location -> Environment -> XMLBuilder encodeEnvironment b loc env0 | nullEnv env = return () - | otherwise = element "context" $ do - forM_ (keysEnv env) $ \k -> do + | otherwise = element "context" $ + forM_ (keysEnv env) $ \k -> element "item" $ do "name" .=. k case lookupEnv k env of @@ -310,18 +313,18 @@ encodeDiagnosis :: Monad m => Bool -> (a -> m XMLBuilder) -> Diagnosis a -> m XMLBuilder encodeDiagnosis mode f diagnosis = case diagnosis of - Buggy r -> return $ element "buggy" $ "ruleid" .=. Rule.name r + Buggy r -> return $ element "buggy" $ "ruleid" .=. showId r NotEquivalent -> return $ tag "notequiv" Similar b s -> ok "similar" b s Nothing - Expected b s r -> ok "expected" b s (Just r) - Detour b s r -> ok "detour" b s (Just r) + Expected b s r -> ok "expected" b s (Just (showId r)) + Detour b s r -> ok "detour" b s (Just (showId r)) Correct b s -> ok "correct" b s Nothing where ok t b s mr = do body <- encodeState mode f s return $ element t $ do "ready" .=. map toLower (show b) - maybe (return ()) (("ruleid" .=.) . Rule.name) mr + maybe (return ()) ("ruleid" .=.) mr body encodeContext :: Monad m => Bool -> (a -> m XMLBuilder) -> Context a -> m XMLBuilder
src/Service/ProblemDecomposition.hs view
@@ -11,101 +11,85 @@ ----------------------------------------------------------------------------- module Service.ProblemDecomposition ( problemDecomposition - , Reply, replyToXML, xmlToRequest + , replyType, replyTypeSynonym, encodeReply ) where -import Common.Apply -import Common.Context -import Common.Exercise -import Common.Derivation -import Common.Strategy hiding (not, repeat, fail) -import Common.Transformation +import Common.Library import Common.Utils -import Data.Char +import Control.Monad import Data.Maybe -import Service.TypedAbstractService (State(..), stepsremaining) +import Service.ExercisePackage +import Service.State +import Service.Types import Text.XML hiding (name) -import qualified Text.XML as XML -import Control.Monad -import Text.OpenMath.Object -replyError :: String -> String -> Reply a -replyError kind = Error . ReplyError kind - -problemDecomposition :: State a -> StrategyLocation -> Maybe a -> Reply a -problemDecomposition (State ex mpr requestedTerm) sloc answer +problemDecomposition :: Monad m => Maybe Id -> State a -> Maybe a -> m (Reply a) +problemDecomposition msloc state answer | isNothing $ subStrategy sloc (strategy ex) = - replyError "request error" "invalid location for strategy" + fail "request error: invalid location for strategy" | otherwise = - let pr = fromMaybe (emptyPrefix $ strategy ex) mpr in + let pr = fromMaybe (emptyPrefix $ strategy ex) (statePrefix state) in case (runPrefixLocation sloc pr requestedTerm, maybe Nothing (Just . inContext ex) answer) of - ([], _) -> replyError "strategy error" "not able to compute an expected answer" + ([], _) -> fail "strategy error: not able to compute an expected answer" (answers, Just answeredTerm) - | not (null witnesses) -> - Ok ReplyOk - { repOk_Code = ex - , repOk_Location = nextTaskLocation sloc $ nextMajorForPrefix newPrefix (fst $ head witnesses) - , repOk_Context = show newPrefix ++ ";" ++ - show (getEnvironment $ fst $ head witnesses) - , repOk_Steps = fromMaybe 0 $ stepsremaining $ State ex (Just newPrefix) (fst $ head witnesses) - } + | not (null witnesses) -> return $ + Ok newLocation newState where witnesses = filter (similarityCtx ex answeredTerm . fst) $ take 1 answers - newPrefix = snd (head witnesses) - ((expected, prefix):_, maybeAnswer) -> - Incorrect ReplyIncorrect - { repInc_Code = ex - , repInc_Location = subTaskLocation sloc loc - , repInc_Expected = fromJust (fromContext expected) - , repInc_Derivation = derivation - , repInc_Arguments = args - , repInc_Steps = fromMaybe 0 $ stepsremaining $ State ex (Just pr) requestedTerm - , repInc_Equivalent = maybe False (equivalenceContext ex expected) maybeAnswer - } + (newCtx, newPrefix) = head witnesses + newLocation = nextTaskLocation (strategy ex) sloc $ + fromMaybe topId $ nextMajorForPrefix newPrefix newCtx + newState = makeState pkg (Just newPrefix) newCtx + ((expected, pref):_, maybeAnswer) -> return $ + Incorrect isEquiv newLocation expState arguments where - (loc, args) = firstMajorInPrefix pr prefix requestedTerm - derivation = - let len = length $ prefixToSteps pr - rules = stepsToRules $ drop len $ prefixToSteps prefix - f (s, a) = (s, fromJust (fromContext a)) - in map f (makeDerivation requestedTerm rules) - + newLocation = subTaskLocation (strategy ex) sloc loc + expState = makeState pkg (Just pref) expected + isEquiv = maybe False (equivalenceContext ex expected) maybeAnswer + (loc, arguments) = fromMaybe (topId, []) $ + firstMajorInPrefix pr pref requestedTerm + where + pkg = exercisePkg state + ex = exercise pkg + topId = getId (strategy ex) + sloc = fromMaybe topId msloc + requestedTerm = stateContext state + similarityCtx :: Exercise a -> Context a -> Context a -> Bool similarityCtx ex a b = fromMaybe False $ liftM2 (similarity ex) (fromContext a) (fromContext b) -- | Continue with a prefix until a certain strategy location is reached. At least one -- major rule should have been executed -runPrefixLocation :: StrategyLocation -> Prefix a -> a -> [(a, Prefix a)] -runPrefixLocation loc p0 = - concatMap (check . f) . derivations . +runPrefixLocation :: Id -> Prefix a -> a -> [(a, Prefix a)] +runPrefixLocation loc p0 = + concatMap (checkPair . f) . derivations . cutOnStep (stop . lastStepInPrefix) . prefixTree p0 where f d = (last (terms d), if isEmpty d then p0 else last (steps d)) - stop (Just (End is _)) = is==loc + stop (Just (Exit info)) = getId info == loc stop _ = False - check result@(a, p) + checkPair result@(a, p) | null rules = [result] | all isMinorRule rules = runPrefixLocation loc p a | otherwise = [result] where rules = stepsToRules $ drop (length $ prefixToSteps p0) $ prefixToSteps p -firstMajorInPrefix :: Prefix a -> Prefix a -> a -> (StrategyLocation, Args) -firstMajorInPrefix p0 prefix a = fromMaybe (topLocation, []) $ do - let steps = prefixToSteps prefix - newSteps = drop (length $ prefixToSteps p0) steps +firstMajorInPrefix :: Prefix a -> Prefix a -> a -> Maybe (Id, Args) +firstMajorInPrefix p0 p a = do + let newSteps = drop (length $ prefixToSteps p0) (prefixToSteps p) is <- firstLocation newSteps return (is, argumentsForSteps a newSteps) where - firstLocation :: [Step a] -> Maybe StrategyLocation + firstLocation :: HasId l => [Step l a] -> Maybe Id firstLocation [] = Nothing - firstLocation (Begin is _:Step r:_) | isMajorRule r = Just is + firstLocation (Enter info:RuleStep r:_) | isMajorRule r = Just (getId info) firstLocation (_:rest) = firstLocation rest -argumentsForSteps :: a -> [Step a] -> Args -argumentsForSteps a = flip rec a . stepsToRules +argumentsForSteps :: a -> [Step l a] -> Args +argumentsForSteps a0 = flip rec a0 . stepsToRules where rec [] _ = [] rec (r:rs) a @@ -114,22 +98,15 @@ in maybe [] (zip ds) (expectedArguments r a) | otherwise = [] -nextMajorForPrefix :: Prefix a -> a -> StrategyLocation -nextMajorForPrefix p0 a = fromMaybe topLocation $ do +nextMajorForPrefix :: Prefix a -> a -> Maybe Id +nextMajorForPrefix p0 a = do (_, p1) <- safeHead $ runPrefixMajor p0 a - let steps = prefixToSteps p1 - rec (reverse steps) + rec (reverse (prefixToSteps p1)) where rec [] = Nothing - rec (Begin is _:_) = Just is - rec (End is _:_) = Just is - rec (_:rest) = rec rest - -makeDerivation :: a -> [Rule a] -> [(String, a)] -makeDerivation _ [] = [] -makeDerivation a (r:rs) = - let new = applyD r a - in [ (name r, new) | isMajorRule r ] ++ makeDerivation new rs + rec (Enter info:_) = Just (getId info) + rec (Exit info:_) = Just (getId info) + rec (_:rest) = rec rest -- Copied from TypedAbstractService: clean me up runPrefixMajor :: Prefix a -> a -> [(a, Prefix a)] @@ -137,154 +114,55 @@ map f . derivations . cutOnStep (stop . lastStepInPrefix) . prefixTree p0 where f d = (last (terms d), if isEmpty d then p0 else last (steps d)) - stop (Just (Step r)) = isMajorRule r + stop (Just (RuleStep r)) = isMajorRule r stop _ = False - ------------------------------------------------------------------------- --- Requests - -extractString :: String -> XML -> Either String String -extractString s = liftM getData . findChild s - -xmlToRequest :: XML -> (OMOBJ -> Maybe a) -> Exercise a -> Either String (State a, StrategyLocation, Maybe a) -xmlToRequest xml fromOpenMath ex = do - unless (XML.name xml == "request") $ - fail "XML document is not a request" - loc <- optional (extractLocation "location" xml) - term <- extractExpr "term" xml - context <- optional (extractString "context" xml) - answer <- optional (extractExpr "answer" xml) - t <- maybe (fail "invalid omobj") return (fromOpenMath term) - mt <- case answer of - Nothing -> return Nothing - Just o -> return $ fromOpenMath o - return - ( State - { exercise = ex - , prefix = case context of - Just s -> Just $ getPrefix2 s (strategy ex) - Nothing -> Just $ emptyPrefix (strategy ex) - , context = case context of - Just s -> putInContext2 ex s t - Nothing -> inContext ex t - } - , fromMaybe topLocation loc - , mt - ) - ------------------------------------------------------------ -putInContext2 :: Exercise a -> String -> a -> Context a -putInContext2 ex s = fromMaybe (inContext ex) $ do - (_, s2) <- splitAtElem ';' s - env <- parseContext s2 - return (makeContext ex env) - -getPrefix2 :: String -> LabeledStrategy (Context a) -> Prefix (Context a) -getPrefix2 s ls = fromMaybe (emptyPrefix ls) $ do - (s1, _) <- splitAtElem ';' s - is <- readM s1 - makePrefix is ls - -optional :: Either String a -> Either String (Maybe a) -optional = Right . either (const Nothing) Just - -extractLocation :: String -> XML -> Either String StrategyLocation -extractLocation s xml = do - c <- findChild s xml - case parseStrategyLocation (getData c) of - Just loc -> return loc - _ -> fail "invalid location" - -extractExpr :: String -> XML -> Either String OMOBJ -extractExpr n xml = - case findChild n xml of - Just expr -> - case children expr of - [this] -> xml2omobj this - _ -> fail $ "error in " ++ show (n, xml) - _ -> fail $ "error in " ++ show (n, xml) - --- Legacy code: remove! -parseContext :: String -> Maybe Environment -parseContext s - | all isSpace s = - return emptyEnv - | otherwise = do - pairs <- mapM (splitAtElem '=') (splitsWithElem ',' s) - let env = foldr (uncurry storeEnv) emptyEnv pairs - return env ------------------------------------------------------------------------ -- Data types for replies --- There are three possible replies: ok, incorrect, or an error in the protocol (e.g., a parse error) -data Reply a = Ok (ReplyOk a) | Incorrect (ReplyIncorrect a) | Error ReplyError - -data ReplyOk a = ReplyOk - { repOk_Code :: Exercise a - , repOk_Location :: StrategyLocation - , repOk_Context :: String - , repOk_Steps :: Int - } - -data ReplyIncorrect a = ReplyIncorrect - { repInc_Code :: Exercise a - , repInc_Location :: StrategyLocation - , repInc_Expected :: a - , repInc_Derivation :: [(String, a)] - , repInc_Arguments :: Args - , repInc_Steps :: Int - , repInc_Equivalent :: Bool - } - -data ReplyError = ReplyError - { repErr_Kind :: String - , repErr_Message :: String - } +data Reply a = Ok Id (State a) + | Incorrect Bool Id (State a) Args type Args = [(String, String)] ------------------------------------------------------------------------ -- Conversion functions to XML -replyToXML :: (a -> OMOBJ) -> Reply a -> XML -replyToXML toOpenMath reply = +encodeReply :: Monad m => (State a -> m XMLBuilder) -> Reply a -> m XMLBuilder +encodeReply showState reply = case reply of - Ok r -> replyOkToXML r - Incorrect r -> replyIncorrectToXML toOpenMath r - Error r -> replyErrorToXML r + Ok loc state -> do + stateXML <- showState state + return $ + element "correct" $ do + element "location" (text $ show loc) + stateXML + Incorrect b loc state args -> do + stateXML <- showState state + return $ + element "incorrect" $ do + "equivalent" .=. show b + element "location" (text $ show loc) + stateXML + let f (x, y) = element "elem" $ do + "descr" .=. x + text y + unless (null args) $ + element "arguments" $ mapM_ f args -replyOkToXML :: ReplyOk a -> XML -replyOkToXML r = makeReply "ok" $ do - element "strategy" (text $ show $ exerciseCode $ repOk_Code r) - element "location" (text $ show $ repOk_Location r) - element "context" (text $ repOk_Context r) - element "steps" (text $ show $ repOk_Steps r) +replyType :: Type a (Reply a) +replyType = useSynonym replyTypeSynonym -replyIncorrectToXML :: (a -> OMOBJ) -> ReplyIncorrect a -> XML -replyIncorrectToXML toOpenMath r = makeReply "incorrect" $ do - element "strategy" (text $ show $ exerciseCode $ repInc_Code r) - element "location" (text $ show $ repInc_Location r) - element "expected" (builder $ omobj2xml $ toOpenMath $ repInc_Expected r) - element "steps" (text $ show $ repInc_Steps r) - element "equivalent" (text $ show $ repInc_Equivalent r) +replyTypeSynonym :: TypeSynonym a (Reply a) +replyTypeSynonym = typeSynonym "DecompositionReply" to from tp + where + to (Left (a, b)) = Ok a b + to (Right (a, b, c, d)) = Incorrect a b c d - unless (null $ repInc_Arguments r) $ - let f (x, y) = element "elem" $ do - "descr" .=. x - text y - in element "arguments" $ mapM_ f (repInc_Arguments r) - - unless (null $ repInc_Derivation r) $ - let f (x,y) = element "elem" $ do - "ruleid" .=. x - builder (omobj2xml (toOpenMath y)) - in element "derivation" $ mapM_ f (repInc_Derivation r) - -replyErrorToXML :: ReplyError -> XML -replyErrorToXML r = makeReply (repErr_Kind r) (text $ repErr_Message r) + from (Ok a b) = Left (a, b) + from (Incorrect a b c d) = Right (a, b, c, d) -makeReply :: String -> XMLBuilder -> XML -makeReply kind body = makeXML "reply" $ do - "result" .=. kind - body+ tp = tuple2 Id stateTp + :|: tuple4 Bool Id stateTp argsTp + + argsTp = List (Pair String String)
src/Service/Request.hs view
@@ -11,12 +11,12 @@ ----------------------------------------------------------------------------- module Service.Request where -import Common.Exercise+import Common.Library hiding (exerciseId) import Data.Char data Request = Request { service :: String- , exerciseID :: Maybe ExerciseCode+ , exerciseId :: Maybe Id , source :: Maybe String , dataformat :: DataFormat , encoding :: Maybe Encoding
src/Service/RulesInfo.hs view
@@ -10,51 +10,41 @@ -- ----------------------------------------------------------------------------- module Service.RulesInfo - ( RulesInfo, mkRulesInfo, rulesInfoXML- , rewriteRuleToFMP, collectExamples+ ( rulesInfoXML, rewriteRuleToFMP, collectExamples, ExampleMap, rulesInfoType ) where +import Common.Library import Common.Utils (Some(..))-import Common.Context-import Common.Derivation-import Common.Exercise hiding (getRule)-import Common.Rewriting-import Common.Strategy (derivationTree)-import Common.Transformation import Data.Char import Control.Monad import Text.OpenMath.Object import Text.OpenMath.FMP import Text.XML hiding (name) import Service.ExercisePackage (termToOMOBJ)+import Service.Types import qualified Data.Map as M -data RulesInfo a = I--mkRulesInfo :: RulesInfo a-mkRulesInfo = I- rulesInfoXML :: Monad m => Exercise a -> (a -> m XMLBuilder) -> m XMLBuilder rulesInfoXML ex enc = combine $ forM (ruleset ex) $ \r -> do - let pairs = M.findWithDefault [] (name r) exampleMap- examples <- forM (take 3 pairs) $ \(a, b) ->- liftM2 (,) (enc a) (enc b)+ let pairs = M.findWithDefault [] (getId r) exampleMap+ xs <- forM (take 3 pairs) $ \(a, b) ->+ liftM2 (,) (enc a) (enc b) return $ element "rule" $ do- "name" .=. name r+ "name" .=. showId r "buggy" .=. f (isBuggyRule r) "rewriterule" .=. f (isRewriteRule r) -- More information- let descr = ruleDescription r+ let descr = description r -- to do: rules should carry descriptions - txt = if null descr then (name r) else descr + txt = if null descr then showId r else descr unless (null txt) $ element "description" $ text txt forM_ (ruleGroups r) $ \s -> - element "group" $ text s+ element "group" $ text (showId s) forM_ (ruleSiblings r) $ \s -> - element "sibling" $ text s+ element "sibling" $ text $ showId s -- FMPs and CMPs forM_ (getRewriteRules r) $ \(Some rr, b) -> do let fmp = rewriteRuleToFMP b rr@@ -64,7 +54,7 @@ element "FMP" $ builder (omobj2xml (toObject fmp)) -- Examples- forM_ examples $ \(a, b) ->+ forM_ xs $ \(a, b) -> element "example" (a >> b) where f = map toLower . show@@ -76,13 +66,18 @@ | sound = eqFMP a b | otherwise = buggyFMP a b where- a :~> b = fmap termToOMOBJ (rulePair r 0)- -collectExamples :: Exercise a -> M.Map String [(a, a)]+ a :~> b = fmap termToOMOBJ (ruleSpecTerm r)++type ExampleMap a = M.Map Id [(a, a)]++collectExamples :: Exercise a -> ExampleMap a collectExamples ex = foldr add M.empty (examples ex) where add a m = let tree = derivationTree (strategy ex) (inContext ex a) f Nothing = m f (Just d) = foldr g m (zip3 (terms d) (steps d) (drop 1 (terms d)))- g (a, r, b) = M.insertWith (++) (name r) (liftM2 (,) (fromContext a) (fromContext b))+ g (x, r, y) = M.insertWith (++) (getId r) (liftM2 (,) (fromContext x) (fromContext y)) in f (derivation tree) ++rulesInfoType :: Type a ()+rulesInfoType = useSynonym (typeSynonym "RulesInfo" id id Unit)
src/Service/ServiceList.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XRankNTypes #-}+{-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -10,32 +10,21 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Service.ServiceList - ( serviceList, exerciselistS- , Service(..), evalService- ) where+module Service.ServiceList (serviceList, exerciselistS) where -import Common.Exercise hiding (Exercise)-import Common.Strategy (toStrategy)-import Common.Transformation+import Common.Library hiding (apply, applicable, derivation) import Common.Utils (Some(..)) import Data.List (sortBy)-import Service.FeedbackText hiding (ExerciseText)-import Service.ProblemDecomposition+import Data.Ord+import Service.FeedbackText+import Service.ProblemDecomposition (problemDecomposition, replyType) import Service.ExercisePackage+import Service.Types import Service.RulesInfo-import Service.Types -import qualified Common.Exercise as E-import qualified Service.Diagnose as S-import qualified Service.Submit as S-import qualified Service.TypedAbstractService as S--data Service = Service - { serviceName :: String- , serviceDescription :: String- , serviceDeprecated :: Bool- , serviceFunction :: forall a . TypedValue a- }+import Service.State+import Service.BasicServices+import qualified Service.Diagnose as Diagnose+import qualified Service.Submit as Submit ------------------------------------------------------ -- Querying a service@@ -43,22 +32,14 @@ serviceList :: [Service] serviceList = [ derivationS, allfirstsS, onefirstS, readyS- , stepsremainingS, applicableS, applyS, generateS- , submitS, diagnoseS+ , stepsremainingS, applicableS, allapplicationsS+ , applyS, generateS+ , examplesS, submitS, diagnoseS , onefirsttextS, findbuggyrulesS , submittextS, derivationtextS , problemdecompositionS , rulelistS, rulesinfoS, strategyinfoS ]--makeService :: String -> String -> (forall a . TypedValue a) -> Service-makeService name descr f = Service name descr False f--deprecate :: Service -> Service-deprecate s = s { serviceDeprecated = True }--evalService :: Monad m => Evaluator m inp out a -> Service -> inp -> m out-evalService f = eval f . serviceFunction ------------------------------------------------------ -- Basic services@@ -69,7 +50,7 @@ \current expression. The first optional argument lets you configure the \ \strategy, i.e., make some minor modifications to it. Rules used and \ \intermediate expressions are returned in a list." $ - S.derivation ::: Maybe StrategyCfg :-> State :-> Error (List (tuple2 Rule Context))+ derivation ::: maybeTp StrategyCfg :-> stateTp :-> errorTp (List (tuple2 Rule Context)) allfirstsS :: Service allfirstsS = makeService "allfirsts" @@ -77,7 +58,7 @@ \onefirst service to get only one suggestion. For each suggestion, a new \ \state, the rule used, and the location where the rule was applied are \ \returned." $ - S.allfirsts ::: State :-> Error (List (tuple3 Rule Location State))+ allfirsts ::: stateTp :-> errorTp (List (tuple3 Rule Location stateTp)) onefirstS :: Service onefirstS = makeService "onefirst" @@ -85,55 +66,70 @@ \service to get all possible steps that are allowed by the strategy. In \ \addition to a new state, the rule used and the location where to apply \ \this rule are returned." $ - S.onefirst ::: State :-> Elem (Error (tuple3 Rule Location State))+ onefirst ::: stateTp :-> elemTp (errorTp (tuple3 Rule Location stateTp)) readyS :: Service readyS = makeService "ready" "Test if the current expression is in a form accepted as a final answer. \ \For this, the strategy is not used." $ - S.ready ::: State :-> Bool+ ready ::: stateTp :-> Bool stepsremainingS :: Service stepsremainingS = makeService "stepsremaining" "Computes how many steps are remaining to be done, according to the \ \strategy. For this, only the first derivation is considered, which \ \corresponds to the one returned by the derivation service." $- S.stepsremaining ::: State :-> Error Int+ stepsremaining ::: stateTp :-> errorTp Int applicableS :: Service applicableS = makeService "applicable" "Given a current expression and a location in this expression, this service \ \yields all rules that can be applied at this location, regardless of the \ \strategy." $ - S.applicable ::: Location :-> State :-> List Rule+ applicable ::: Location :-> stateTp :-> List Rule +allapplicationsS :: Service+allapplicationsS = makeService "allapplications" + "Given a current expression, this service yields all rules that can be \+ \applied at a certain location, regardless wether the rule used is buggy \+ \or not. Some results are within the strategys, others are not." $ + allapplications ::: stateTp :-> List (tuple3 Rule Location stateTp)+ applyS :: Service applyS = makeService "apply" "Apply a rule at a certain location to the current expression. If this rule \ \was not expected by the strategy, we deviate from it. If the rule cannot \ \be applied, this service call results in an error." $ - S.apply ::: Rule :-> Location :-> State :-> Error State+ apply ::: Rule :-> Location :-> stateTp :-> errorTp stateTp generateS :: Service generateS = makeService "generate" "Given an exercise code and a difficulty level (optional), this service \ \returns an initial state with a freshly generated expression. The meaning \ \of the difficulty level (an integer) depends on the exercise at hand." $ - S.generate ::: Exercise :-> Optional 5 Int :-> IO State+ generate ::: ExercisePkg :-> optionTp 5 Int :-> IO stateTp +examplesS :: Service+examplesS = makeService "examples"+ "This services returns a list of example expresssions that can be solved \+ \with an exercise. These are the examples that appear at the page generated \+ \for each exercise. Also see the generate service, which returns a random \+ \start term." $+ (examples . exercise) ::: ExercisePkg :-> List Term+ findbuggyrulesS :: Service findbuggyrulesS = makeService "findbuggyrules" "Search for common misconceptions (buggy rules) in an expression (compared \ \to the current state). It is assumed that the expression is indeed not \ \correct. This service has been superseded by the diagnose service." $ - S.findbuggyrules ::: State :-> Term :-> List Rule+ findbuggyrules ::: stateTp :-> Term :-> List Rule submitS :: Service submitS = deprecate $ makeService "submit" "Analyze an expression submitted by a student. Possible answers are Buggy, \ \NotEquivalent, Ok, Detour, and Unknown. This service has been superseded \ \by the diagnose service." $ - S.submit ::: State :-> Term :-> Result+ Submit.submit ::: stateTp :-> Term :-> Submit.submitType diagnoseS :: Service diagnoseS = makeService "diagnose" @@ -145,7 +141,7 @@ \expression was not expected by the strategy, but the applied rule was \ \detected), and Correct (it is correct, but we don't know which rule was \ \applied)." $- S.diagnose ::: State :-> Term :-> Diagnosis+ Diagnose.diagnose ::: stateTp :-> Term :-> Diagnose.diagnosisType ------------------------------------------------------ -- Services with a feedback component@@ -157,7 +153,7 @@ \leading to this service call (which can influence the returned result). \ \The boolean in the result specifies whether a suggestion was available or \ \not." $ - onefirsttext ::: ExerciseText :-> State :-> Maybe String :-> Elem (tuple3 Bool String State)+ onefirsttext ::: stateTp :-> maybeTp String :-> errorTp (elemTp (tuple3 Bool String stateTp)) submittextS :: Service submittextS = makeService "submittext" @@ -167,24 +163,25 @@ \for announcing the event leading to this service call. The boolean in the \ \result specifies whether the submitted term is accepted and incorporated \ \in the new state." $ - submittext ::: ExerciseText :-> State :-> String :-> Maybe String :-> Elem (tuple3 Bool String State)+ submittext ::: stateTp :-> String :-> maybeTp String :-> errorTp (elemTp (tuple3 Bool String stateTp)) derivationtextS :: Service derivationtextS = makeService "derivationtext" "Similar to the derivation service, but the rules appearing in the derivation \ \have been replaced by a short description of the rule. The optional string is \ \for announcing the event leading to this service call." $ - derivationtext ::: ExerciseText :-> State :-> Maybe String :-> Error (List (tuple2 String Context))+ derivationtext ::: stateTp :-> maybeTp String :-> errorTp (List (tuple2 String Context)) ------------------------------------------------------ -- Problem decomposition service + problemdecompositionS :: Service problemdecompositionS = makeService "problemdecomposition" "Strategy service developed for the SURF project Intelligent Feedback for a \ \binding with the MathDox system on linear algebra exercises. This is a \ \composite service, and available for backwards compatibility." $- problemDecomposition ::: State :-> StrategyLoc :-> Maybe Term :-> DecompositionReply+ problemDecomposition ::: maybeTp Id :-> stateTp :-> maybeTp (Tag "answer" Term) :-> errorTp replyType ------------------------------------------------------ -- Reflective services@@ -193,7 +190,7 @@ exerciselistS list = makeService "exerciselist" "Returns all exercises known to the system. For each exercise, its domain, \ \identifier, a short description, and its current status are returned." $- allExercises list ::: List (tuple4 (Tag "domain" String) (Tag "identifier" String) (Tag "description" String) (Tag "status" String))+ allExercises list ::: List (tuple3 (Tag "exerciseid" String) (Tag "description" String) (Tag "status" String)) rulelistS :: Service rulelistS = makeService "rulelist" @@ -201,30 +198,30 @@ \name (or identifier), whether the rule is buggy, and whether the rule was \ \expressed as an observable rewrite rule. See rulesinfo for more details \ \about the rules." $ - allRules ::: Exercise :-> List (tuple3 (Tag "name" String) (Tag "buggy" Bool) (Tag "rewriterule" Bool))+ allRules ::: ExercisePkg :-> List (tuple3 (Tag "name" String) (Tag "buggy" Bool) (Tag "rewriterule" Bool)) rulesinfoS :: Service rulesinfoS = makeService "rulesinfo" "Returns a list of all rules of a particular exercise, with many details \ \including Formal Mathematical Properties (FMPs) and example applications." $- mkRulesInfo ::: RulesInfo+ () ::: rulesInfoType strategyinfoS :: Service strategyinfoS = makeService "strategyinfo" "Returns the representation of the strategy of a particular exercise." $ - (toStrategy . strategy) ::: Exercise :-> Strategy + (toStrategy . strategy . exercise) ::: ExercisePkg :-> Strategy -allExercises :: [Some ExercisePackage] -> [(String, String, String, String)]-allExercises = map make . sortBy cmp+allExercises :: [Some ExercisePackage] -> [(String, String, String)]+allExercises = map make . sortBy (comparing f) where- cmp e1 e2 = f e1 `compare` f e2- f (Some pkg) = exerciseCode (exercise pkg)+ f (Some pkg) = showId (exercise pkg) make (Some pkg) = - let ex = exercise pkg- code = exerciseCode ex - in (domain code, identifier code, description ex, show (status ex))+ (showId pkg, description pkg, show (status (exercise pkg))) -allRules :: E.Exercise a -> [(String, Bool, Bool)]-allRules = map make . ruleset+allRules :: ExercisePackage a -> [(String, Bool, Bool)]+allRules = map make . ruleset . exercise where - make r = (name r, isBuggyRule r, isRewriteRule r)+ make r = (showId r, isBuggyRule r, isRewriteRule r)+ +elemTp :: Type a t -> Type a t+elemTp = Tag "elem"
+ src/Service/State.hs view
@@ -0,0 +1,72 @@+----------------------------------------------------------------------------- +-- Copyright 2010, 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) +-- +-- The information maintained for a learner trying to complete a +-- derivation. +-- +----------------------------------------------------------------------------- +module Service.State + ( -- * Exercise state + State, makeState, empyStateContext, emptyState + , exercisePkg, statePrefix, stateContext, stateTerm + -- * Types + , stateTp, stateTypeSynonym + ) where + +import Common.Library +import Common.Utils (readM) +import Service.Types +import Data.Maybe +import Service.ExercisePackage + +data State a = State + { exercisePkg :: ExercisePackage a + , statePrefix :: Maybe (Prefix (Context a)) + , stateContext :: Context a + } + +stateTerm :: State a -> a +stateTerm = fromMaybe (error "invalid term") . fromContext . stateContext + +----------------------------------------------------------- + +makeState :: ExercisePackage a -> Maybe (Prefix (Context a)) -> Context a -> State a +makeState = State + +empyStateContext :: ExercisePackage a -> Context a -> State a +empyStateContext pkg = makeState pkg (Just pr) + where + ex = exercise pkg + pr = emptyPrefix (strategy ex) + +emptyState :: ExercisePackage a -> a -> State a +emptyState pkg = empyStateContext pkg . inContext (exercise pkg) + +-------------------------------------------------------------- + +stateTp :: Type a (State a) +stateTp = useSynonym stateTypeSynonym + +stateTypeSynonym :: TypeSynonym a (State a) +stateTypeSynonym = typeSynonym "State" to from tp + where + to (pkg, mp, ctx) = + let str = strategy (exercise pkg) + f = fromMaybe [] . readM + in makeState pkg (mp >>= flip makePrefix str . f) ctx + from st = + ( exercisePkg st + , fmap show (statePrefix st) + , stateContext st + ) + tp = tuple3 ExercisePkg prefixTp Context + +prefixTp :: Type a (Maybe String) +prefixTp = Tag "Prefix" (maybeTp String)
src/Service/StrategyInfo.hs view
@@ -16,20 +16,12 @@ import Data.Char import Data.Maybe import Control.Monad+import Common.Library import Common.Strategy.Core (Core(..), noLabels) import Common.Strategy.Abstract import Text.XML-import Common.Transformation hiding (name) import Common.Utils (readInt) -instance InXML (LabeledStrategy a) where- toXML = toXML . toStrategy- fromXML xml = fromXML xml >>= toLabeledStrategy--instance InXML (Strategy a) where- toXML = strategyToXML- fromXML = xmlToStrategy unknownRule- ----------------------------------------------------------------------- -- Strategy to XML @@ -38,7 +30,7 @@ infoToXML :: LabelInfo -> XMLBuilder infoToXML info = do- "name" .=. labelName info+ "name" .=. showId info when (removed info) ("removed" .=. "true") when (collapsed info) ("collapsed" .=. "true") when (hidden info) ("hidden" .=. "true")@@ -49,7 +41,7 @@ Label l a -> infoToXML l >> coreBuilder infoToXML a _ -> coreBuilder infoToXML core -coreBuilder :: (l -> XMLBuilder) -> Core l a -> XMLBuilder+coreBuilder :: HasId l => (l -> XMLBuilder) -> Core l a -> XMLBuilder coreBuilder f = rec where rec core = @@ -59,10 +51,11 @@ _ :|>: _ -> asList "orelse" isOrElse Many a -> element "many" (rec a) Repeat a -> element "repeat" (rec a)+ Label l (Rule r) | getId l == getId r -> element "rule" (f l) Label l a -> element "label" (f l >> rec a) Rec n a -> element "rec" (("var" .=. show n) >> rec a) Not a -> element "not" (recNot a)- Rule l r -> element "rule" (maybe ("name" .=. show r) f l)+ Rule r -> element "rule" ("name" .=. show r) Var n -> element "var" ("var" .=. show n) Succeed -> tag "succeed" Fail -> tag "fail"@@ -92,9 +85,9 @@ xmlToStrategy :: Monad m => (String -> Maybe (Rule a)) -> XML -> m (Strategy a) xmlToStrategy f = liftM fromCore . readStrategy xmlToInfo g where- g info = case f (labelName info) of+ g info = case f (showId info) of Just r -> return r- Nothing -> fail $ "Unknown rule: " ++ show (labelName info) + Nothing -> fail $ "Unknown rule: " ++ showId info xmlToInfo :: Monad m => XML -> m LabelInfo xmlToInfo xml = do@@ -114,14 +107,9 @@ "false" -> return False _ -> fail "not a boolean" -unknownRule :: Monad m => String -> m (Rule a)-unknownRule s = - let n = "#Unknown rule:" ++ s- in return (makeSimpleRule n (const Nothing))- readStrategy :: Monad m => (XML -> m l) -> (l -> m (Rule a)) -> XML -> m (Core l a)-readStrategy f g xml = do- xs <- mapM (readStrategy f g) (children xml)+readStrategy toLabel findRule xml = do+ xs <- mapM (readStrategy toLabel findRule) (children xml) let s = name xml case lookup s table of Just f -> f s xs@@ -138,12 +126,12 @@ | null xs = return Fail | otherwise = return (foldr1 (:|>:) xs) buildLabel x = do- info <- f xml+ info <- toLabel xml return (Label info x) buildRule = do- info <- f xml- rule <- g info- return (Rule (Just info) rule)+ info <- toLabel xml+ r <- findRule info+ return (Label info (Rule r)) buildRec x = do s <- findAttribute "var" xml i <- maybe (fail "var: not an int") return (readInt s)@@ -153,11 +141,11 @@ i <- maybe (fail "var: not an int") return (readInt s) return (Var i) - nullary a _ [] = return a- nullary _ s _ = fail $ "Strategy combinator " ++ s ++ "expects 0 args"+ comb0 a _ [] = return a+ comb0 _ s _ = fail $ "Strategy combinator " ++ s ++ "expects 0 args" - unary f _ [x] = return (f x)- unary _ s _ = fail $ "Strategy combinator " ++ s ++ "expects 1 arg"+ comb1 f _ [x] = return (f x)+ comb1 _ s _ = fail $ "Strategy combinator " ++ s ++ "expects 1 arg" join2 f g a b = join (f g a b) @@ -165,13 +153,13 @@ [ ("sequence", buildSequence) , ("choice", buildChoice) , ("orelse", buildOrElse)- , ("many", unary Many)- , ("repeat", unary Repeat)- , ("label", join2 unary buildLabel)- , ("rec", join2 unary buildRec)- , ("not", unary (Not . noLabels))- , ("rule", join2 nullary buildRule)- , ("var", join2 nullary buildVar)- , ("succeed", nullary Succeed)- , ("fail", nullary Fail) + , ("many", comb1 Many)+ , ("repeat", comb1 Repeat)+ , ("label", join2 comb1 buildLabel)+ , ("rec", join2 comb1 buildRec)+ , ("not", comb1 (Not . noLabels))+ , ("rule", join2 comb0 buildRule)+ , ("var", join2 comb0 buildVar)+ , ("succeed", comb0 Succeed)+ , ("fail", comb0 Fail) ]
src/Service/Submit.hs view
@@ -11,13 +11,16 @@ -- Diagnose a term submitted by a student. Deprecated (see diagnose service). -- ------------------------------------------------------------------------------module Service.Submit (submit, Result(..), getResultState) where+module Service.Submit + ( submit, Result(..), getResultState+ , submitType, submitTypeSynonym+ ) where -import Common.Transformation-import Common.Context+import Common.Library import qualified Service.Diagnose as Diagnose import Service.Diagnose (Diagnosis, diagnose)-import Service.TypedAbstractService+import Service.State+import Service.Types -- Note that in the typed setting there is no syntax error data Result a = Buggy [Rule (Context a)] @@ -27,8 +30,8 @@ | Unknown (State a) -- equivalent fromDiagnose :: Diagnosis a -> Result a-fromDiagnose diagnose =- case diagnose of+fromDiagnose diagnosis =+ case diagnosis of Diagnose.Buggy r -> Buggy [r] Diagnose.NotEquivalent -> NotEquivalent Diagnose.Similar _ s -> Ok [] s@@ -37,7 +40,7 @@ Diagnose.Correct _ s -> Unknown s submit :: State a -> a -> Result a -submit state new = fromDiagnose (diagnose state new)+submit state = fromDiagnose . diagnose state getResultState :: Result a -> Maybe (State a) getResultState result =@@ -46,3 +49,27 @@ Detour _ st -> return st Unknown st -> return st _ -> Nothing+ +submitType :: Type a (Result a)+submitType = useSynonym submitTypeSynonym++submitTypeSynonym :: TypeSynonym a (Result a)+submitTypeSynonym = typeSynonym "Result" to from tp+ where+ to (Left rs) = Buggy rs+ to (Right (Left ())) = NotEquivalent+ to (Right (Right (Left (rs, s)))) = Ok rs s+ to (Right (Right (Right (Left (rs, s))))) = Detour rs s+ to (Right (Right (Right (Right s)))) = Unknown s++ from (Buggy rs) = Left rs+ from (NotEquivalent) = Right (Left ())+ from (Ok rs s) = Right (Right (Left (rs, s)))+ from (Detour rs s) = Right (Right (Right (Left (rs, s))))+ from (Unknown s) = Right (Right (Right (Right s))) ++ tp = List Rule + :|: Unit+ :|: Pair (List Rule) stateTp+ :|: Pair (List Rule) stateTp+ :|: stateTp
− src/Service/TypedAbstractService.hs
@@ -1,152 +0,0 @@------------------------------------------------------------------------------ --- Copyright 2010, 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.TypedAbstractService - ( -- * Exercise state - State(..), emptyState, term - -- * Services - , stepsremaining, findbuggyrules, ready, allfirsts, derivation - , onefirst, applicable, apply, generate, generateWith - ) where - -import qualified Common.Apply as Apply -import Common.Context -import Common.Derivation hiding (derivation) -import Common.Exercise (Exercise(..), ruleset, randomTermWith, inContext) -import Common.Strategy hiding (not, fail) -import Common.Transformation (Rule, name, isMajorRule, isBuggyRule) -import Common.Utils (safeHead) -import Common.Navigator -import Data.Maybe -import System.Random -import Control.Monad - -data State a = State - { exercise :: Exercise a - , prefix :: Maybe (Prefix (Context a)) - , context :: Context a - } - -term :: State a -> a -term = fromMaybe (error "invalid term") . fromContext . context - ------------------------------------------------------------ - -emptyState :: Exercise a -> a -> State a -emptyState ex a = State - { exercise = ex - , prefix = Just (emptyPrefix (strategy ex)) - , context = inContext ex a - } - --- result must be in the IO monad to access a standard random number generator -generate :: Exercise a -> Int -> IO (State a) -generate ex level = do - stdgen <- newStdGen - return (generateWith stdgen ex level) - -generateWith :: StdGen -> Exercise a -> Int -> State a -generateWith rng ex level = emptyState ex (randomTermWith rng level ex) - -derivation :: Monad m => Maybe StrategyConfiguration -> State a -> m [(Rule (Context a), Context a)] -derivation mcfg state = - case (prefix state, mcfg) of - (Nothing, _) -> fail "Prefix is required" - -- configuration is only allowed beforehand: hence, the prefix - -- should be empty (or else, the configuration is ignored). This - -- restriction should probably be relaxed later on. - (Just p, Just cfg) | null (prefixToSteps p) -> - let new = configure cfg $ strategy $ exercise state - in rec state - { prefix = Just (emptyPrefix new) - , exercise = (exercise state) {strategy=new} - } - _ -> rec state - where - rec :: Monad m => State a -> m [(Rule (Context a), Context a)] - rec state = do - xs <- allfirsts state - case xs of - [] -> return [] - (r, _, next):_ -> liftM ((r, context next):) (rec next) - --- Note that we have to inspect the last step of the prefix afterwards, because --- the remaining part of the derivation could consist of minor rules only. -allfirsts :: Monad m => State a -> m [(Rule (Context a), Location, State a)] -allfirsts state = - case prefix state of - Nothing -> - fail "Prefix is required" - Just p0 -> - let tree = cutOnStep (stop . lastStepInPrefix) (prefixTree p0 (context state)) - in return (mapMaybe make (derivations tree)) - where - stop (Just (Step r)) = isMajorRule r - stop _ = False - - make d = do - prefixEnd <- safeHead (reverse (steps d)) - termEnd <- safeHead (reverse (terms d)) - case lastStepInPrefix prefixEnd of - Just (Step r) | isMajorRule r -> return - ( r - , location termEnd - , state { context = termEnd - , prefix = Just prefixEnd - } - ) - _ -> Nothing - -onefirst :: Monad m => State a -> m (Rule (Context a), Location, State a) -onefirst state = - case allfirsts state of - Right (hd:_) -> return hd - Right [] -> fail "No step possible" - Left msg -> fail msg - -applicable :: Location -> State a -> [Rule (Context a)] -applicable loc state = - let check r = not (isBuggyRule r) && Apply.applicable r (setLocation loc (context state)) - in filter check (ruleset (exercise state)) - --- local helper -setLocation :: Location -> Context a -> Context a -setLocation loc c0 = fromMaybe c0 $ do - navigateTo loc c0 - --- Two possible scenarios: either I have a prefix and I can return a new one (i.e., still following the --- strategy), or I return a new term without a prefix. A final scenario is that the rule cannot be applied --- to the current term at the given location, in which case the request is invalid. -apply :: Monad m => Rule (Context a) -> Location -> State a -> m (State a) -apply r loc state = maybe applyOff applyOn (prefix state) - where - applyOn _ = -- scenario 1: on-strategy - maybe applyOff return $ safeHead - [ s1 | (r1, loc1, s1) <- fromMaybe [] $ allfirsts state, name r == name r1, loc==loc1 ] - - applyOff = -- scenario 2: off-strategy - case Apply.apply r (setLocation loc (context state)) of - Just new -> return state { context=new, prefix=Nothing } - Nothing -> fail ("Cannot apply " ++ show r) - -ready :: State a -> Bool -ready state = isReady (exercise state) (term state) - -stepsremaining :: Monad m => State a -> m Int -stepsremaining = liftM length . derivation Nothing - -findbuggyrules :: State a -> a -> [Rule (Context a)] -findbuggyrules state a = - let ex = exercise state - isA = maybe False (similarity ex a) . fromContext - buggies = filter isBuggyRule (ruleset ex) - check r = any isA (Apply.applyAll r (context state)) - in filter check buggies
src/Service/TypedExample.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XGADTs #-}+{-# LANGUAGE GADTs #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -16,40 +16,40 @@ import Service.DomainReasoner import Service.ModeXML import Service.ExercisePackage-import Service.ServiceList+import Service.Evaluator import Service.Types-import Common.Exercise+import Common.Library import Text.XML typedExample :: ExercisePackage a -> Service -> [TypedValue a] -> DomainReasoner (XML, XML, Bool) typedExample pkg service args = do -- Construct a request in xml- xmlRequest <- + request <- case makeArgType args of Nothing -> return $ - stdReply (serviceName service) enc (exercise pkg) (return ())- Just (reqTuple ::: reqTp) ->- case encodeType (encoder evaluator) reqTp reqTuple of- Left err -> fail err- Right xml -> return $ - stdReply (serviceName service) enc (exercise pkg) xml+ stdReply (showId service) enc (exercise pkg) (return ())+ Just (reqTuple ::: reqTp) -> do+ xml <- encodeType (encoder evaluator) reqTp reqTuple+ return $ + stdReply (showId service) enc (exercise pkg) xml -- Construct a reply in xml- xmlReply <- return $+ reply <- case foldl dynamicApply (serviceFunction service) args of- reply ::: replyTp ->- case encodeType (encoder evaluator) replyTp reply of- Left err -> resultError err- Right xml -> resultOk xml+ reply ::: replyTp -> do+ xml <- encodeType (encoder evaluator) replyTp reply+ return (resultOk xml) + `catchError` + (return . resultError) -- Check request/reply pair vers <- getVersion xmlTest <- do- (_, txt, _) <- processXML (show xmlRequest)+ (_, txt, _) <- processXML (show request) let p = filter (not . isSpace)- out = showXML (if null vers then xmlReply else addVersion vers xmlReply)+ out = showXML (if null vers then reply else addVersion vers reply) return (p txt == p out) `catchError` const (return False)- return (xmlRequest, xmlReply, xmlTest)+ return (request, reply, xmlTest) where (evaluator, enc) | withOpenMath pkg = (openMathConverterTp pkg, "openmath")@@ -58,14 +58,14 @@ stdReply :: String -> String -> Exercise a -> XMLBuilder -> XML stdReply s enc ex body = makeXML "request" $ do "service" .=. s- "exerciseid" .=. show (exerciseCode ex)+ "exerciseid" .=. showId ex "source" .=. "test" "encoding" .=. enc body makeArgType :: [TypedValue a] -> Maybe (TypedValue a) makeArgType [] = fail "makeArgType: empty list"-makeArgType [_ ::: Exercise] = fail "makeArgType: empty list"+makeArgType [_ ::: ExercisePkg] = fail "makeArgType: empty list" makeArgType [tv] = return tv makeArgType ((a1 ::: t1) : rest) = do a2 ::: t2 <- makeArgType rest@@ -79,13 +79,3 @@ Just eq -> f (eq a) ::: t2 Nothing -> error $ "mismatch (argument type): " ++ show t3 ++ " does not match " ++ show t1 _ -> error "mismatch (not a function)"--equal :: Type a t1 -> Type a t2 -> Maybe (t1 -> t2)-equal t1 t2 = - case (t1, t2) of- (Maybe a, Maybe b) -> fmap fmap (equal a b)- (StrategyCfg, StrategyCfg) -> Just id- (State, State) -> Just id- (Location, Location) -> Just id- (Exercise, Exercise) -> Just id- _ -> Nothing
src/Service/Types.hs view
@@ -10,26 +10,78 @@ -- Portability : portable (depends on ghc) -- ------------------------------------------------------------------------------module Service.Types where+module Service.Types + ( -- * Services+ Service, makeService, deprecate + , serviceDeprecated, serviceFunction+ -- * Types+ , Type(..), TypedValue(..), tuple2, tuple3, tuple4, maybeTp, optionTp+ , errorTp, equal, isSynonym, useSynonym, TypeSynonym, typeSynonym+ , equalM+ ) where -import Common.Context (Context, fromContext)-import Common.Exercise (Exercise)-import Common.Navigator (Location)-import Common.Transformation (Rule, name)-import Common.Strategy (Strategy, StrategyLocation, StrategyConfiguration)+import Common.Library import Common.Utils (commaList)-import Control.Arrow import Control.Monad import Data.Maybe-import Service.ExercisePackage (ExercisePackage, exercise, getExerciseText)-import Service.TypedAbstractService (State)-import Service.Submit (Result)-import Service.Diagnose (Diagnosis)-import Service.FeedbackText (ExerciseText)-import Service.RulesInfo-import System.IO.Unsafe-import qualified Service.ProblemDecomposition as Decomposition+import Service.ExercisePackage +-----------------------------------------------------------------------------+-- Services++data Service = Service + { serviceId :: Id+ , serviceDeprecated :: Bool+ , serviceFunction :: forall a . TypedValue a+ }+ +instance HasId Service where+ getId = serviceId+ changeId f a = a { serviceId = f (serviceId a) }+ +makeService :: String -> String -> (forall a . TypedValue a) -> Service+makeService s descr f = describe descr (Service (newId s) False f)++deprecate :: Service -> Service+deprecate s = s { serviceDeprecated = True }++equalM :: Monad m => Type a t1 -> Type a t2 -> m (t1 -> t2)+equalM t1 t2 = maybe (fail msg) return (equal t1 t2)+ where msg = "Types not equal: " ++ show t1 ++ " and " ++ show t2++equal :: Type a t1 -> Type a t2 -> Maybe (t1 -> t2)+equal type1 type2 =+ case (type1, type2) of+ (Pair a b, Pair c d ) -> equalPairs a b c d+ (a :|: b, c :|: d ) -> equalChoice a b c d+ (List a, List b ) -> liftM map (equal a b)+ (Rule, Rule ) -> Just id+ (Unit, Unit ) -> Just id+ (StrategyCfg, StrategyCfg) -> Just id+ (Location, Location ) -> Just id+ (Id, Id ) -> Just id+ (Term, Term ) -> Just id+ (ExercisePkg, ExercisePkg) -> Just id+ (Context, Context ) -> Just id+ (Bool, Bool ) -> Just id+ (String, String ) -> Just id+ (Int, Int ) -> Just id+ (Iso _ f a, _ ) -> fmap (. f) (equal a type2)+ (_, Iso f _ b ) -> fmap (f .) (equal type1 b)+ (Tag s1 a, Tag s2 b ) -> guard (s1==s2) >> equal a b+ _ -> Nothing+ where+ equalPairs a b c d = + liftM2 (\f g (x, y) -> (f x, g y)) (equal a c) (equal b d)+ + equalChoice a b c d =+ liftM2 (\f g -> either (Left . f) (Right . g)) (equal a c) (equal b d)++infixr 5 :|:++-----------------------------------------------------------------------------+-- Types+ infix 2 ::: infixr 3 :-> @@ -50,6 +102,21 @@ f (a, (b, (c, d))) = (a, b, c, d) g (a, b, c, d) = (a, (b, (c, d))) +maybeTp :: Type a t1 -> Type a (Maybe t1)+maybeTp t = Iso f g (t :|: Unit)+ where+ f = either Just (const Nothing)+ g = maybe (Right ()) Left++optionTp :: t1 -> Type a t1 -> Type a t1+optionTp a t = Iso (fromMaybe a) Just (maybeTp t)++errorTp :: Type a t -> Type a (Either String t)+errorTp t = Iso f g (t :|: IO Unit)+ where+ f = either Right (const (Left "errorTp"))+ g = either (Right . fail) Left+ data Type a t where -- Type isomorphisms (for defining type synonyms) Iso :: (t1 -> t2) -> (t2 -> t1) -> Type a t1 -> Type a t2@@ -57,29 +124,21 @@ (:->) :: Type a t1 -> Type a t2 -> Type a (t1 -> t2) -- Special annotations Tag :: String -> Type a t1 -> Type a t1- Optional :: t1 -> Type a t1 -> Type a t1- Maybe :: Type a t1 -> Type a (Maybe t1)- Error :: Type a t -> Type a (Either String t) -- Type constructors List :: Type a t -> Type a [t] Pair :: Type a t1 -> Type a t2 -> Type a (t1, t2)- Elem :: Type a t -> Type a t -- quick fix+ (:|:) :: Type a t1 -> Type a t2 -> Type a (Either t1 t2)+ Unit :: Type a () IO :: Type a t -> Type a (IO t) -- Exercise-specific types- State :: Type a (State a)- Exercise :: Type a (Exercise a)+ ExercisePkg :: Type a (ExercisePackage a) Strategy :: Type a (Strategy (Context a))- ExerciseText :: Type a (ExerciseText a) Rule :: Type a (Rule (Context a))- RulesInfo :: Type a (RulesInfo a) Term :: Type a a Context :: Type a (Context a)- Result :: Type a (Result a)- Diagnosis :: Type a (Diagnosis a) Location :: Type a Location- StrategyLoc :: Type a StrategyLocation+ Id :: Type a Id StrategyCfg :: Type a StrategyConfiguration- DecompositionReply :: Type a (Decomposition.Reply a) -- Basic types Bool :: Type a Bool Int :: Type a Int@@ -89,17 +148,14 @@ show (Iso _ _ t) = show t show (t1 :-> t2) = show t1 ++ " -> " ++ show t2 show t@(Pair _ _) = showTuple t- show (Tag _ t) = show t- show (Optional _ t) = "(" ++ show t ++ ")?"- show (Maybe t) = "(" ++ show t ++ ")?"- show (Error t) = show t+ show (t1 :|: t2) = show t1 ++ " | " ++ show t2+ show (Tag s _) = s -- ++ "@(" ++ show t ++ ")" show (List t) = "[" ++ show t ++ "]"- show (Elem t) = show t show (IO t) = show t show t = fromMaybe "unknown" (groundType t) showTuple :: Type a t -> String-showTuple t = "(" ++ commaList (collect t) ++ ")"+showTuple tp = "(" ++ commaList (collect tp) ++ ")" where collect :: Type a t -> [String] collect (Pair t1 t2) = collect t1 ++ collect t2@@ -109,98 +165,42 @@ groundType :: Type a t -> Maybe String groundType tp = case tp of - State -> Just "State"- Exercise -> Just "Exercise"+ ExercisePkg -> Just "ExercisePkg" Strategy -> Just "Strategy"- ExerciseText -> Just "ExerciseText" Rule -> Just "Rule"- RulesInfo -> Just "RulesInfo" Term -> Just "Term" Context -> Just "Context"- Result -> Just "Result"- Diagnosis -> Just "Diagnosis"+ Unit -> Just "()" Bool -> Just "Bool" Int -> Just "Int" String -> Just "String" Location -> Just "Location"- StrategyLoc -> Just "StrategyLocation"+ Id -> Just "Id" StrategyCfg -> Just "StrategyConfiguration" _ -> Nothing--data Evaluator m inp out a = Evaluator - { encoder :: Encoder m out a- , decoder :: Decoder m inp a- }+ +-----------------------------------------------------------------------------+-- Type Synonyms -data Encoder m s a = Encoder - { encodeType :: forall t . Type a t -> t -> m s- , encodeTerm :: a -> m s- , encodeTuple :: [s] -> s+data TypeSynonym a t = TS + { synonymName :: String + , useSynonym :: Type a t+ , isSynonym :: Monad m => TypedValue a -> m t }--data Decoder m s a = Decoder - { decodeType :: forall t . Type a t -> s -> m (t, s)- , decodeTerm :: s -> m a- , decoderPackage :: ExercisePackage a+ +typeSynonym :: String -> (t2 -> t) -> (t -> t2) -> Type a t2 -> TypeSynonym a t+typeSynonym name to from tp = TS+ { synonymName = name+ , useSynonym = Tag name (Iso to from tp)+ , isSynonym = maybe (fail name) return . matchSynonym }--decoderExercise :: Decoder m s a -> Exercise a-decoderExercise = exercise . decoderPackage--eval :: Monad m => Evaluator m inp out a -> TypedValue a -> inp -> m out-eval f (tv ::: tp) s = - case tp of - t1 :-> t2 -> do- (a, s1) <- decodeType (decoder f) t1 s- eval f (tv a ::: t2) s1- _ ->- encodeType (encoder f) tp tv--decodeDefault :: MonadPlus m => Decoder m s a -> Type a t -> s -> m (t, s)-decodeDefault dec tp s =- case tp of- Iso f _ t -> liftM (first f) (decodeType dec t s)- Pair t1 t2 -> do- (a, s1) <- decodeType dec t1 s- (b, s2) <- decodeType dec t2 s1- return ((a, b), s2)- Tag _ t1 ->- decodeType dec t1 s- Optional a t1 -> - decodeType dec t1 s `mplus` return (a, s)- Maybe t1 -> - liftM (first Just) (decodeType dec t1 s) `mplus` return (Nothing, s)- Error t -> - liftM (first Right) (decodeType dec t s)- Exercise -> do- return (exercise (decoderPackage dec), s)- ExerciseText -> do- exText <- case getExerciseText (decoderPackage dec) of - Just a -> return a- Nothing -> fail "No support for exercise texts"- return (exText, s)- _ ->- fail $ "No support for argument type: " ++ show tp+ where+ matchSynonym (a ::: t0) = do+ (s, t) <- isTag t0+ guard (s == name)+ f <- equal t tp+ return (to (f a)) -encodeDefault :: Monad m => Encoder m s a -> Type a t -> t -> m s-encodeDefault enc tp tv =- case tp of- Iso _ f t -> encodeType enc t (f tv)- Pair t1 t2 -> do- let (a, b) = tv- x <- encodeType enc t1 a- y <- encodeType enc t2 b- return (encodeTuple enc [x, y])- Tag _ t1 -> encodeType enc t1 tv- Elem t1 -> encodeType enc t1 tv- Optional _ t1 -> encodeType enc t1 tv- Maybe t1 -> case tv of- Just a -> encodeType enc t1 a- Nothing -> return (encodeTuple enc [])- Error t -> either fail (encodeType enc t) tv- IO t1 -> encodeType enc t1 (unsafePerformIO tv)- Rule -> encodeType enc String (name tv)- Term -> encodeTerm enc tv- Context -> fromContext tv >>= encodeType enc Term- Location -> encodeType enc String (show tv)- _ -> fail ("No support for result type: " ++ show tp)+isTag :: Type a t -> Maybe (String, Type a t)+isTag (Tag s t) = Just (s, t)+isTag _ = Nothing
src/Text/HTML.hs view
@@ -13,14 +13,17 @@ ----------------------------------------------------------------------------- module Text.HTML ( HTML, HTMLBuilder, showHTML- , htmlPage, errorPage, link, h1, h2, h3, h4, preText, ul, table, noBorderTable- , text, image, space, tt, spaces- , bold, italic, para, ttText, hr, br, pre, center, bullet+ , htmlPage, errorPage, link, linkTitle+ , h1, h2, h3, h4, preText, ul, table+ , text, image, space, tt, spaces, highlightXML+ , font, bold, italic, para, ttText, hr, br, pre, center, bullet, divClass ) where import Text.XML hiding (text) import qualified Text.XML as XML import Control.Monad+import Data.Char+import Data.List type HTML = XML @@ -33,7 +36,8 @@ htmlPage :: String -> Maybe String -> HTMLBuilder -> HTML htmlPage title css body = makeXML "html" $ do element "head" $ do- element "title" (text title)+ unless (null title) $+ element "title" (text title) case css of Nothing -> return () Just n -> element "link" $ do@@ -51,6 +55,10 @@ link url body = element "a" $ ("href" .=. url) >> body +linkTitle :: String -> String -> HTMLBuilder -> HTMLBuilder+linkTitle url title body = element "a" $ + ("href" .=. url) >> ("title" .=. title) >> body+ center :: HTMLBuilder -> HTMLBuilder center = element "center" @@ -66,6 +74,8 @@ h4 :: String -> HTMLBuilder h4 = element "h4" . text +font :: String -> HTMLBuilder -> HTMLBuilder+font n = element "font" . ("class" .=. n >>) bold, italic :: HTMLBuilder -> HTMLBuilder bold = element "b" @@ -98,12 +108,15 @@ table :: [[HTMLBuilder]] -> HTMLBuilder table rows = element "table" $ do "border" .=. "1"- mapM_ (element "tr" . mapM_ (element "td")) rows--noBorderTable :: [[HTMLBuilder]] -> HTMLBuilder-noBorderTable rows = element "table" $ do- "border" .=. "0"- mapM_ (element "tr" . mapM_ (element "td")) rows+ forM_ (zip [0::Int ..] rows) $ \(i, r) ->+ element "tr" $ do+ "class" .=. getClass i+ mapM_ (element "td") r+ where+ getClass i+ | i == 0 = "topRow"+ | even i = "evenRow"+ | otherwise = "oddRow" spaces :: Int -> HTMLBuilder spaces n = replicateM_ n space@@ -117,3 +130,36 @@ text :: String -> HTMLBuilder text = XML.text++divClass :: String -> HTMLBuilder -> HTMLBuilder+divClass n body = element "div" ("class" .=. n >> body)++-- A simple XML highlighter+highlightXML :: Bool -> XML -> HTMLBuilder+highlightXML nice+ | nice = builder . highlight . makeXML "pre" . text . showXML+ | otherwise = builder . highlight . makeXML "tt" . text . compactXML+ where+ highlight :: HTML -> HTML+ highlight html = html {content = map (either (Left . f) Right) (content html)}+ + -- find <+ f :: String -> String+ f [] = []+ f list@(x:xs)+ | "</" `isPrefixOf` list = -- close tag+ let (as, bs) = span isAlphaNum (drop 5 list) + in "<font color='blue'></" ++ as ++ "<font color='green'>" ++ g bs+ | "<" `isPrefixOf` list = -- open tag+ let (as, bs) = span isAlphaNum (drop 4 list) + in "<font color='blue'><" ++ as ++ "<font color='green'>" ++ g bs+ | otherwise = x : f xs+ -- find >+ g [] = []+ g list@(x:xs) + | "/>" `isPrefixOf` list =+ "</font>/></font>" ++ f (drop 5 list)+ | ">" `isPrefixOf` list =+ "</font>></font>" ++ f (drop 4 list)+ | x=='=' = "<font color='orange'>=</font>" ++ g xs+ | otherwise = x : g xs
src/Text/JSON.hs view
@@ -17,7 +17,7 @@ , InJSON(..) -- type class" , lookupM , parseJSON, showCompact, showPretty -- parser and pretty-printers - , jsonRPC, JSON_RPC_Handler, testMe + , jsonRPC, JSON_RPC_Handler, propEncoding ) where import Text.Parsing @@ -228,7 +228,7 @@ lookupM _ _ = fail "expecting a JSON object" indent :: Int -> String -> String -indent n = unlines . map (\s -> replicate n ' ' ++ s) . lines +indent n = unlines . map (replicate n ' ' ++) . lines -------------------------------------------------------- -- JSON-RPC over HTTP @@ -255,18 +255,18 @@ instance CoArbitrary JSON where coarbitrary json = case json of - Number a -> variant 0 . coarbitrary a - String s -> variant 1 . coarbitrary s - Boolean b -> variant 2 . coarbitrary b - Array xs -> variant 3 . coarbitrary xs - Object xs -> variant 4 . coarbitrary xs - Null -> variant 5 + Number a -> variant (0 :: Int) . coarbitrary a + String s -> variant (1 :: Int) . coarbitrary s + Boolean b -> variant (2 :: Int) . coarbitrary b + Array xs -> variant (3 :: Int) . coarbitrary xs + Object xs -> variant (4 :: Int) . coarbitrary xs + Null -> variant (5 :: Int) instance Arbitrary Number where arbitrary = oneof [liftM I arbitrary, liftM (D . fromInteger) arbitrary] instance CoArbitrary Number where - coarbitrary (I n) = variant 0 . coarbitrary n - coarbitrary (D d) = variant 1 . coarbitrary d + coarbitrary (I n) = variant (0 :: Int) . coarbitrary n + coarbitrary (D d) = variant (1 :: Int) . coarbitrary d arbJSON :: Int -> Gen JSON arbJSON n @@ -293,10 +293,6 @@ replicateM n $ oneof $ map return $ ['A' .. 'Z'] ++ ['a' .. 'z'] ++ ['0' .. '9'] -testMe :: IO () -testMe = do - putStrLn "** JSON encoding" - quickCheck prop - where - prop :: JSON -> Bool - prop a = parseJSON (show a) == Just a+propEncoding :: Property +propEncoding = property $ \a -> + parseJSON (show a) == Just a
src/Text/OpenMath/Object.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS -XDeriveDataTypeable #-} +{-# LANGUAGE DeriveDataTypeable #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, @@ -15,6 +15,7 @@ ) where import Data.Char (isSpace) +import Data.Generics.Uniplate hiding (children) import Data.List (nub) import Data.Maybe import Data.Typeable @@ -35,22 +36,24 @@ toXML = omobj2xml fromXML = either fail return . xml2omobj +instance Uniplate OMOBJ where + uniplate omobj = + case omobj of + OMA xs -> (xs, OMA) + OMBIND a ss b -> ([a, b], \[x, y] -> OMBIND x ss y) + _ -> ([], \_ -> omobj) + getOMVs :: OMOBJ -> [String] -getOMVs = nub . rec - where - rec (OMA xs) = concatMap rec xs - rec (OMBIND q _ b) = rec q ++ rec b - rec (OMV s) = [s] - rec _ = [] +getOMVs omobj = nub [ x | OMV x <- universe omobj ] ---------------------------------------------------------- -- conversion functions: XML <-> OMOBJ xml2omobj :: XML -> Either String OMOBJ -xml2omobj xml = - case xml of +xml2omobj xmlTop = + case xmlTop of Element "OMOBJ" _ [Right e] -> rec e - _ -> fail $ "expected an OMOBJ tag" ++ show xml + _ -> fail $ "expected an OMOBJ tag" ++ show xmlTop where rec xml = case content xml of @@ -61,8 +64,8 @@ [] | name xml == "OMS" -> do let mcd = findAttribute "cd" xml - name <- findAttribute "name" xml - return (OMS (Symbol mcd name)) + n <- findAttribute "name" xml + return (OMS (Symbol mcd n)) [Left s] | name xml == "OMI" -> case scanInt (Pos 0 0) s of
+ src/Text/OpenMath/Tests.hs view
@@ -0,0 +1,50 @@+-----------------------------------------------------------------------------+-- Copyright 2010, 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 Text.OpenMath.Tests (propEncoding) where++import Control.Monad+import Text.OpenMath.Object+import Test.QuickCheck+import Text.OpenMath.Dictionary.Arith1+import Text.OpenMath.Dictionary.Calculus1+import Text.OpenMath.Dictionary.Fns1+import Text.OpenMath.Dictionary.Linalg2+import Text.OpenMath.Dictionary.List1+import Text.OpenMath.Dictionary.Logic1+import Text.OpenMath.Dictionary.Nums1+import Text.OpenMath.Dictionary.Quant1+import Text.OpenMath.Dictionary.Relation1+import Text.OpenMath.Dictionary.Transc1++arbOMOBJ :: Gen OMOBJ+arbOMOBJ = sized rec + where+ symbols = arith1List ++ calculus1List ++ fns1List ++ linalg2List +++ list1List ++ logic1List ++ nums1List ++ quant1List ++ + relation1List ++ transc1List+ + rec 0 = frequency + [ (1, liftM OMI arbitrary)+ , (1, liftM OMF arbitrary)+ , (1, liftM OMV arbitrary)+ , (5, oneof $ map (return . OMS) symbols)+ ]+ rec n = frequency+ [ (1, rec 0)+ , (3, choose (1,4) >>= liftM OMA . (`replicateM` f))+ , (1, liftM3 OMBIND f arbitrary f)+ ]+ where+ f = rec (n `div` 2)++propEncoding :: Property+propEncoding = forAll arbOMOBJ $ \x -> xml2omobj (omobj2xml x) == Right x
src/Text/Parsing.hs view
@@ -24,7 +24,7 @@ -- * Derived token parsers , pParens, pBracks, pCurly, pCommas, pLines, pInteger -- * UU parser combinators - , (<$>), (<$), (<*>), (*>), (<*), (<|>), optional, pList, pList1 + , (<$>), (<$), (<*>), (*>), (<*), (<|>), optional, pList, pList1, pSepList , pChainl, pChainr, pChoice, pFail -- * Operator table (parser) , OperatorTable, Associativity(..), pOperators @@ -48,8 +48,6 @@ -- | A parser with tokens as symbol type type TokenParser = Parser Token -instance UU.Symbol Token - instance (UU.Symbol s, Ord s) => UU.IsParser (Parser s) s where ~(P p) <*> ~(P q) = P (p UU.<*> q) ~(P p) <* ~(P q) = P (p UU.<* q) @@ -107,19 +105,31 @@ pQConid = makeTokT isTokenQConId TokenQConId pString = makeTokS isTokenString TokenString pInt = makeTokN isTokenInt TokenInt -pReal = makeTokN isTokenReal TokenReal +pReal = makeTokR isTokenReal TokenReal pKey = makeTokA TokenKeyword pSpec = makeTokA TokenSpecial -- helpers -makeTokS f con = makeTok f "" (con minString) (con maxString) -makeTokT f con = makeTok f ("","") (con minString minString) (con maxString maxString) -makeTokN f con = makeTok f 0 (con minBound) (con maxBound) -makeTokA con a = makeTok (const Nothing) a (con a) (con a) +makeTokS :: (Token -> Maybe a) -> (String -> Pos -> Token) -> TokenParser a +makeTokS f con = makeTok (fromJust . f) (con minString) (con maxString) -makeTok f a con1 con2 = - (fromMaybe a . f) UU.<$> con1 minPos UU.<..> con2 maxPos +makeTokT :: (Token -> Maybe a) -> (String -> String -> Pos -> Token) -> TokenParser a +makeTokT f con = makeTok (fromJust . f) (con minString minString) (con maxString maxString) +makeTokN :: (Token -> Maybe Int) -> (Int -> Pos -> Token) -> TokenParser Int +makeTokN f con = makeTok (fromJust . f) (con minBound) (con maxBound) + +makeTokR :: (Token -> Maybe Double) -> (Double -> Pos -> Token) -> TokenParser Double +makeTokR f con = makeTok (fromJust . f) (con (-d)) (con d) + where d = (10 :: Double) ^ (500 :: Int) + +makeTokA :: (a -> Pos -> Token) -> a -> TokenParser a +makeTokA con a = makeTok (const a) (con a) (con a) + +makeTok :: (Token -> a) -> (Pos -> Token) -> (Pos -> Token) -> TokenParser a +makeTok f con1 con2 = + f UU.<$> con1 minPos UU.<..> con2 maxPos + minPos, maxPos :: Pos minPos = Pos minBound minBound maxPos = Pos maxBound maxBound @@ -128,14 +138,6 @@ minString = [] maxString = replicate 100 maxBound -minDouble, maxDouble :: Double -minDouble = -(10^500) -- -Infinity -maxDouble = 10^500 -- Infinity - -instance Bounded Double where - minBound = minDouble - maxBound = maxDouble - ---------------------------------------------------------- -- Derived token parsers @@ -178,10 +180,10 @@ (*>) = (UU.*>) (<*) :: (Ord s, UU.Symbol s) => Parser s a -> Parser s b -> Parser s a -(<*) a = (UU.<*) a +(<*) = (UU.<*) (<|>) :: (Ord s, UU.Symbol s) => Parser s a -> Parser s a -> Parser s a -(<|>) a = (UU.<|>) a +(<|>) = (UU.<|>) optional :: (Ord s, UU.Symbol s) => Parser s a -> a -> Parser s a optional = UU.opt @@ -190,6 +192,9 @@ pList = UU.pList pList1 = UU.pList1 +pSepList :: (Ord s, UU.Symbol s) => Parser s a -> Parser s b -> Parser s [a] +pSepList p q = (:) <$> p <*> pList (q *> p) + pChainl, pChainr :: (Ord s, UU.Symbol s) => Parser s (a -> a -> a) -> Parser s a -> Parser s a pChainl = UU.pChainl pChainr = UU.pChainr @@ -212,8 +217,8 @@ -- | Construct a parser using an operator table pOperators :: OperatorTable a -> TokenParser a -> TokenParser a -pOperators table p = foldr op p table - where op (a, ops) q = +pOperators table p = foldr combine p table + where combine (a, ops) q = case a of -- The NoMix variant is actually hard to define efficiently. Since we should not mix operators -- that have the same priority, we have to inspect which operator we are dealing with before
src/Text/Scanning.hs view
@@ -29,6 +29,7 @@ import Control.Monad import Data.List import Data.Char+import qualified UU.Parsing as UU ---------------------------------------------------------- -- * Data types@@ -50,6 +51,8 @@ | TokenReal Double Pos deriving (Eq, Ord) +instance UU.Symbol Token+ instance Show Pos where show (Pos l c) = "(" ++ show l ++ "," ++ show c ++ ")" @@ -189,7 +192,7 @@ make f = f s pos : rec newp xs newp = incr (length s) pos _ -> error "unexpected case in scanner"- | isNumber input =+ | isNum input = case scanNumber pos input of Just (Left i, newp, xs) -> TokenInt i pos : rec newp xs Just (Right d, newp, xs) -> TokenReal d pos : rec newp xs@@ -217,16 +220,16 @@ let newp = incr 1 pos in TokenSpecial x pos : rec newp rest - isNumber :: String -> Bool- isNumber ('-':x:_) = isDigit x && unaryMinus scanner- isNumber (x:_) = isDigit x- isNumber _ = False+ isNum :: String -> Bool+ isNum ('-':x:_) = isDigit x && unaryMinus scanner+ isNum (x:_) = isDigit x+ isNum _ = False scanIdentifier :: Scanner -> String -> Maybe (Maybe String, String, String) scanIdentifier scanner (x:rest) | isAlpha x = - case break (not . isIdentifierCharacter scanner) rest of+ case span (isIdentifierCharacter scanner) rest of (xs, '.':y:rest2) | qualifiedIdentifiers scanner && isAlpha y -> - let (ys, zs) = break (not . isIdentifierCharacter scanner) rest2+ let (ys, zs) = span (isIdentifierCharacter scanner) rest2 in Just (Just (x:xs), y:ys, zs) (xs, ys) -> Just (Nothing, x:xs, ys)@@ -259,7 +262,7 @@ fractionPart _ _ = Nothing powerPart :: Pos -> String -> Maybe (Pos, String)-powerPart pos (s:rest) | s == 'e' || s == 'E' = do+powerPart pos (s:rest) | s `elem` "eE" = do (_, p, ys) <- scanInt pos rest return (incr 1 p, ys) powerPart _ _ = Nothing@@ -273,7 +276,7 @@ scanNatural :: Pos -> String -> Maybe (Int, Pos, String) scanNatural pos input = do- let (xs, ys) = break (not . isDigit) input+ let (xs, ys) = span isDigit input guard (not (null xs)) let nat = foldl' (\a b -> a*10+ord b-48) 0 xs return (nat, incr (length xs) pos, ys)
src/Text/UTF8.hs view
@@ -13,7 +13,7 @@ ----------------------------------------------------------------------------- module Text.UTF8 ( encode, encodeM, decode, decodeM- , isUTF8, allBytes, testEncoding+ , isUTF8, allBytes, propEncoding ) where import Data.Char@@ -105,10 +105,8 @@ -- Test encoding -- | QuickCheck internal encoding/decoding functions-testEncoding :: IO () -testEncoding = do- putStrLn "** UTF8 encoding"- quickCheck $ forAll (sized gen) valid+propEncoding :: Property+propEncoding = forAll (sized gen) valid where gen n = replicateM n someChar someChar = liftM chr $ oneof
src/Text/XML.hs view
@@ -63,7 +63,7 @@ where rec i (Element n as xs) = let ipl = i+2 - cd n = Left ('\n' : replicate n ' ') + cd j = Left ('\n' : replicate j ' ') f = either (\x -> [cd ipl, Left x]) (\x -> [cd ipl, Right (rec ipl x)]) body | null xs = xs | otherwise = concatMap f xs ++ [cd i]
src/Text/XML/Interface.hs view
@@ -21,7 +21,8 @@ import Control.Monad.Error () import qualified Text.XML.Document as D import System.FilePath (takeDirectory, pathSeparator)-import Data.Char (chr)+import Data.Char (chr, ord)+import Data.Maybe data Element = Element { name :: Name@@ -64,9 +65,7 @@ refToContent :: D.Reference -> Content refToContent (D.CharRef i) = [Left [chr i]] refToContent (D.EntityRef s) = - case lookup s entities of- Just c -> c- Nothing -> undefined -- [] -- error+ fromJust (lookup s entities) entities :: [(String, Content)] entities = @@ -92,13 +91,21 @@ where toElement :: Element -> D.Element toElement (Element n as c) =- D.Element n (map toAttribute as) (map toXML c)+ D.Element n (map toAttribute as) (concatMap toXML c) toAttribute :: Attribute -> D.Attribute toAttribute (n := s) = (D.:=) n (map Left s) - toXML :: Either String Element -> D.XML- toXML = either D.CharData (D.Tagged . toElement)+ toXML :: Either String Element -> [D.XML]+ toXML = either fromString (return . D.Tagged . toElement)+ + fromString :: String -> [D.XML]+ fromString [] = []+ fromString xs@(hd:tl) + | null xs1 = D.Reference (D.CharRef (ord hd)) : fromString tl+ | otherwise = D.CharData xs1 : fromString xs2+ where+ (xs1, xs2) = break ((> 127) . ord) xs -----------------------------------------------------
src/Text/XML/ParseLib.hs view
@@ -62,11 +62,11 @@ P.notFollowedBy (P.try (string xs >> return ' ')) return x -symbol :: Char -> Parser Char-symbol = P.char+symbol :: Char -> Parser ()+symbol c = P.char c >> return () -string :: String -> Parser String-string = P.string+string :: String -> Parser ()+string s = P.string s >> return () ranges :: [(Char, Char)] -> Parser Char ranges xs = P.choice [ a <..> b | (a, b) <- xs ]
src/Text/XML/Parser.hs view
@@ -13,8 +13,7 @@ -- http://www.w3.org/TR/2006/REC-xml-20060816 -------------------------------------------------------------------------------module Text.XML.Parser where+module Text.XML.Parser (document, extParsedEnt, extSubset) where import Prelude hiding (seq) import Control.Monad@@ -41,7 +40,7 @@ -- [1] document ::= prolog element Misc* document :: Parser XMLDoc document = do - (mxml, dtd) <- prolog+ (mxml, mdtd) <- prolog rt <- element miscs let (ver, enc, sa) = @@ -52,7 +51,7 @@ { D.versionInfo = ver , D.encoding = enc , D.standalone = sa- , D.dtd = dtd+ , D.dtd = mdtd , D.externals = [] , root = rt }@@ -61,19 +60,21 @@ -- ** 2.2 Characters -- [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]+{- char :: Parser Char char = ranges xs <|> oneOf "\x9\xA\xD" where xs = [('\x20', '\xD7FF'), ('\xE000', '\xFFFD'), ('\x10000', '\x10FFFF')]+-} -------------------------------------------------- -- ** 2.3 Common Syntactic Constructs -- [3] S ::= (#x20 | #x9 | #xD | #xA)+-space :: Parser String-space = many1 (oneOf "\x20\x9\xA\xD")+space :: Parser ()+space = many1 (oneOf "\x20\x9\xA\xD") >> return () -mspace :: Parser String -- for S?-mspace = many (oneOf "\x20\x9\xA\xD")+mspace :: Parser () -- for S?+mspace = many (oneOf "\x20\x9\xA\xD") >> return () -- [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender nameChar :: Parser Char@@ -86,17 +87,21 @@ cs <- many nameChar return (c:cs) +{- -- [6] Names ::= Name (#x20 Name)* names :: Parser [String] names = sepBy1 name (symbol '\x20')+-} -- [7] Nmtoken ::= (NameChar)+ nmtoken :: Parser String nmtoken = many1 nameChar +{- -- [8] Nmtokens ::= Nmtoken (#x20 Nmtoken)* nmtokens :: Parser [String] nmtokens = sepBy1 nmtoken (symbol '\x20')+-} -- [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' -- | "'" ([^%&'] | PEReference | Reference)* "'"@@ -128,7 +133,7 @@ where xs = [('a', 'z'), ('A', 'Z'), ('0', '9')] singleQuote- | withSingleQuote = symbol '\''+ | withSingleQuote = symbol '\'' >> return '\'' | otherwise = fail "pubidChar" --------------------------------------------------@@ -153,9 +158,7 @@ pInstr :: Parser String pInstr = packed (string "<?") p (string "?>") where - p = do - piTarget- option "" (space >> stopOn ["?>"])+ p = piTarget >> option "" (space >> stopOn ["?>"]) -- [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) piTarget :: Parser String@@ -216,7 +219,9 @@ -- [26] VersionNum ::= '1.0' versionNum :: Parser String-versionNum = string "1.0"+versionNum = do+ string "1.0"+ return "1.0" -- [27] Misc ::= Comment | PI | S misc :: Parser ()
src/Text/XML/TestSuite.hs view
@@ -17,7 +17,6 @@ import Text.XML.Interface import Text.XML.Document (trim) import Control.Monad.Error-import Data.List import Data.Maybe {-testje = do
src/Text/XML/Unicode.hs view
@@ -22,6 +22,7 @@ data Tree a = Node (Tree a) a (Tree a) | Leaf +isLetter, isExtender, isDigit, isCombiningChar :: Char -> Bool isLetter = checkTree $ makeTree letterMap isExtender = checkTree $ makeTree extenderMap isDigit = checkTree $ makeTree digitMap@@ -47,6 +48,7 @@ f :: Char -> (Char, Char) f c = (c, c) +letterMap :: [(Char, Char)] letterMap = baseCharMap `merge` ideographicMap `merge` controlMap `merge` extraMap merge :: [(Char, Char)] -> [(Char, Char)] -> [(Char, Char)]@@ -55,8 +57,10 @@ | otherwise = y:merge (x:xs) ys merge xs ys = xs++ys +extraMap :: [(Char, Char)] extraMap = map f "\161\170\184\185" +controlMap :: [(Char, Char)] controlMap = [ ('\x7F', '\x84'), ('\x86', '\x9F'), ('\xFDD0', '\xFDDF'), ('\x1FFFE', '\x1FFFF'), ('\x2FFFE', '\x2FFFF'), ('\x3FFFE', '\x3FFFF'), ('\x4FFFE', '\x4FFFF'), ('\x5FFFE', '\x5FFFF'), ('\x6FFFE', '\x6FFFF'),@@ -65,6 +69,7 @@ ('\xDFFFE', '\xDFFFF'), ('\xEFFFE', '\xEFFFF'), ('\xFFFFE', '\xFFFFF'), ('\x10FFFE', '\x10FFFF')] +baseCharMap :: [(Char, Char)] baseCharMap = [ ('\x0041','\x005A'), ('\x0061','\x007A'), ('\x00C0','\x00D6'), ('\x00D8','\x00F6'), ('\x00F8','\x00FF'), ('\x0100','\x0131'), ('\x0134','\x013E'), ('\x0141','\x0148'), ('\x014A','\x017E'), @@ -121,9 +126,11 @@ f '\x212E' , ('\x2180','\x2182'), ('\x3041','\x3094'), ('\x30A1','\x30FA'), ('\x3105','\x312C'), ('\xAC00','\xD7A3') ] +ideographicMap :: [(Char, Char)] ideographicMap = [ ('\x4E00','\x9FA5'), f '\x3007' , ('\x3021','\x3029') ] - ++combiningCharMap :: [(Char, Char)] combiningCharMap = [('\x0300','\x0345'), ('\x0360','\x0361'), ('\x0483','\x0486'), ('\x0591','\x05A1'), ('\x05A3','\x05B9'), ('\x05BB','\x05BD'), f '\x05BF' , ('\x05C1','\x05C2'), @@ -151,6 +158,7 @@ ('\x0FB1','\x0FB7'), f '\x0FB9' , ('\x20D0','\x20DC'), f '\x20E1' , ('\x302A','\x302F'), f '\x3099' , f '\x309A' ] +digitMap :: [(Char, Char)] digitMap = [ ('\x0030','\x0039'), ('\x0660','\x0669'), ('\x06F0','\x06F9'), ('\x0966','\x096F'), ('\x09E6','\x09EF'), ('\x0A66','\x0A6F'), ('\x0AE6','\x0AEF'), @@ -158,6 +166,7 @@ ('\x0CE6','\x0CEF'), ('\x0D66','\x0D6F'), ('\x0E50','\x0E59'), ('\x0ED0','\x0ED9'), ('\x0F20','\x0F29')] +extenderMap :: [(Char, Char)] extenderMap = [f '\x00B7' , f '\x02D0' , f '\x02D1' , f '\x0387' , f '\x0640' , f '\x0E46' , f '\x0EC6' , f '\x3005' , ('\x3031','\x3035') , ('\x309D','\x309E'), ('\x30FC','\x30FE') ]