ideas-0.6: src/Domain/Math/Expr/Views.hs
-----------------------------------------------------------------------------
-- 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.Views where
import Prelude hiding (recip, (^))
import Common.View
import Domain.Math.Expr.Data
import Domain.Math.Expr.Symbols
import Data.List (nub)
------------------------------------------------------------
-- Smart constructors
(.+.) :: Expr -> Expr -> Expr
Nat 0 .+. b = b
a .+. Nat 0 = a
a .+. Negate b = a .-. b
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
neg :: Expr -> Expr
neg (Nat 0) = 0
neg (Negate a) = a
neg (a :+: b) = neg a .-. b
neg (a :-: b) = neg a .+. b
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)
a .*. (Nat 1 :/: b) = a ./. b
a .*. b = a :*: b
(./.) :: Expr -> Expr -> Expr
a ./. Nat 1 = a
Negate a ./. b = neg (a ./. b)
a ./. Negate b = neg (a ./. b)
(a :/: b) ./. c = a ./. (b .*. c)
a ./. b = a :/: b
recip :: Expr -> Expr
recip (Nat 1 :/: a) = a
recip a = Nat 1 :/: a
(.^.) :: Expr -> Expr -> Expr
Nat 0 .^. _ = Nat 0
Nat 1 .^. _ = Nat 1
_ .^. Nat 0 = Nat 1
a .^. Nat 1 = a
a .^. b = a ^ b
------------------------------------------------------------
-- Views of binary constructors
plusView :: View Expr (Expr, Expr)
plusView = makeView matchPlus (uncurry (.+.))
where
matchPlus :: Match Expr (Expr, Expr)
matchPlus (a :+: b) = Just (a, b)
matchPlus (a :-: b) = Just (a, neg b)
matchPlus (Negate a) = do (x, y) <- matchPlus a
Just (neg x, neg y)
matchPlus _ = Nothing
timesView :: View Expr (Expr, Expr)
timesView = makeView matchTimes (uncurry (.*.))
where
matchTimes :: Match Expr (Expr, Expr)
matchTimes (a :*: b) = Just (a, b)
matchTimes (Negate a) = do (x, y) <- matchTimes a
Just (neg x, y)
matchTimes _ = Nothing
divView :: View Expr (Expr, Expr)
divView = makeView matchDiv (uncurry (./.))
where
matchDiv :: Match Expr (Expr, Expr)
matchDiv (a :/: b) = Just (a, b)
matchDiv (Negate a) = do (x, y) <- matchDiv a
Just (neg x, y)
matchDiv _ = Nothing
------------------------------------------------------------
-- Some constant views
conView :: View Expr Integer
conView = makeView f fromInteger
where
f (Nat n) = return n
f (Negate e) = fmap negate (f e)
f _ = Nothing
fractionView :: View Expr (Integer, Integer) -- second component is positive
fractionView = divView >>> signs >>> (conView *** conView)
where
signs = makeView (Just . f) id
f (a, Negate b) = f (neg a, b)
f (a, b) = (a, b)
-------------------------------------------------------------
-- Sums and products
sumView :: View Expr [Expr]
sumView = makeView (return . ($ []) . f False) (foldl (.+.) 0)
where
f n (a :+: b) = f n a . f n b
f n (a :-: b) = f n a . f (not n) b
f n (Negate a) = f (not n) a
f n e = if n then (neg e:) else (e:)
simpleProductView :: View Expr (Bool, [Expr])
simpleProductView = makeView (Just . second ($ []) . f) g
where
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)
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) = 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 (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)
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
where
f [] = Just "x" -- exceptional case (e.g., for constants)
f [a] = Just a
f _ = Nothing