packages feed

ideas-0.6: src/Domain/Math/Polynomial/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.Polynomial.Views
   ( polyView, polyViewFor, polyViewWith, polyViewForWith
   , quadraticView, quadraticViewFor, quadraticViewWith, quadraticViewForWith
   , linearView, linearViewFor, linearViewWith, linearViewForWith
   , constantPolyView, linearPolyView, quadraticPolyView, cubicPolyView
   , monomialPolyView, binomialPolyView, trinomialPolyView
   , polyNormalForm
   , linearEquationView, quadraticEquationView, quadraticEquationsView
   , higherDegreeEquationsView
   ) where

import Prelude hiding ((^))
import Control.Monad
import Common.Apply
import Common.View
import Common.Traversable
import Common.Uniplate (transform, uniplate, 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 Data.Maybe
import qualified Domain.Math.Data.SquareRoot as SQ
import Domain.Math.SquareRoot.Views
import Domain.Math.Power.Views (powerFactorViewForWith)

-------------------------------------------------------------------
-- Polynomial view

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)
 where
   f expr = do 
      pv <- selectVar expr
      p  <- match (polyViewForWith pv v) 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 = 
      case expr of
         Var s | pv == s -> Just var
         Nat n    -> Just (fromIntegral n)
         Negate a -> liftM negate (f a)
         a :+: b  -> liftM2 (+) (f a) (f b)
         a :-: b  -> liftM2 (-) (f a) (f b)
         a :*: b  -> liftM2 (*) (f a) (f b)
         a :/: b  -> do
            c <- match v b
            guard (c /= 0)
            guard (pv `notElem` collectVars b)
            p <- f a
            return (fmap (/c) p)
         Sym s [a, n] | s == powerSymbol ->
           liftM2 power (f a) (matchNat n)
         _ -> do 
            guard (pv `notElem` collectVars expr)
            liftM con (match v expr)
   
   g        = build sumView . map h . reverse . terms
   h (a, n) = build v a .*. (Var pv .^. fromIntegral n)
   
   matchNat expr = do
      n <- match integralView expr
      guard (n >= 0)
      return n

-------------------------------------------------------------------
-- Quadratic view

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)

constantPolyView :: Num a => View (Polynomial a) a
constantPolyView = makeView (isList1 . polynomialList) (buildList . list1)
    
linearPolyView :: Num a => View (Polynomial a) (a, a)
linearPolyView = makeView (isList2 . polynomialList) (buildList . list2)
 
quadraticPolyView :: Num a => View (Polynomial a) (a, a, a)
quadraticPolyView = makeView (isList3 . polynomialList) (buildList . list3)
    
cubicPolyView :: Num a => View (Polynomial a) (a, a, a, a)
cubicPolyView = makeView (isList4 . polynomialList) (buildList . list4)

-------------------------------------------------------------------
-- Views on polynomials (number of terms)

monomialPolyView :: Num a => View (Polynomial a) (a, Int)
monomialPolyView = makeView (isList1. terms) (buildPairs . list1)

binomialPolyView :: Num a => View (Polynomial a) ((a, Int), (a, Int))
binomialPolyView = makeView (isList2 . terms) (buildPairs . list2)

trinomialPolyView :: Num a => View (Polynomial a) ((a, Int), (a, Int), (a, Int))
trinomialPolyView = makeView (isList3 . terms) (buildPairs . list3)

-- helpers
buildList :: Num a => [a] -> Polynomial a
buildList = buildPairs . flip zip [0..] . reverse

buildPairs :: Num a => [(a, Int)] -> Polynomial a
buildPairs as 
   | null as   = 0
   | otherwise = foldl1 (+) (map f as)
 where
   f (a, n) = con a * power var n
 
polynomialList :: Num a => Polynomial a -> [a]
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]
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
isList4 [a, b, c, d] = Just (a, b, c, d)
isList4 _            = Nothing

-------------------------------------------------------------------
-- Normal form, and list of power factors

listOfPowerFactors :: Num a => String -> View Expr a -> View Expr [(a, Int)]
listOfPowerFactors pv v = sumView >>> listView (powerFactorViewForWith pv v)

polyNormalForm :: Num a => View Expr a -> View Expr (String, Polynomial a)
polyNormalForm v = makeView f (uncurry g)
 where
   f e = do
      pv <- selectVar e
      xs <- match (listOfPowerFactors pv v) e
      guard (distinct (map snd xs))
      return (pv, buildPairs xs)
   g pv = build (listOfPowerFactors pv v) . reverse . terms
   
-------------------------------------------------------------------
-- Normal forms for equations

-- Excludes equations such as 1==1 or 0==1
linearEquationViewWith :: Fractional a => View Expr a -> View (Equation Expr) (String, a)
linearEquationViewWith v = makeView f g
 where
   f (lhs :==: rhs) = do 
      (x, a, b) <- match (linearViewWith v) (lhs - rhs)
      return (x, -b/a)
   g (x, r) = Var x :==: build v r
   
linearEquationView :: View (Equation Expr) (String, Rational)
linearEquationView = linearEquationViewWith rationalView

quadraticEquationsView:: View (OrList (Equation Expr)) (OrList (String, SQ.SquareRoot Rational))
quadraticEquationsView = makeView f (fmap g)
 where
   f eq = do 
      ors <- switch (fmap (match quadraticEquationView) eq)
      return (normalize (join ors))

   g (x, a) = Var x :==: build (squareRootViewWith rationalView) a

quadraticEquationView :: View (Equation Expr) (OrList (String, SQ.SquareRoot Rational))
quadraticEquationView = makeView f g
 where
   f (lhs :==: rhs) = do
      (s, p) <- match (polyViewWith (squareRootViewWith rationalView)) (lhs - rhs)
      guard (degree p <= 2)
      liftM (fmap ((,) s)) $
         case polynomialList p of
            [a, b, c] -> do
               discr <- SQ.fromSquareRoot (b*b - SQ.scale 4 (a*c))
               let sdiscr = SQ.sqrtRational discr
                   twoA   = SQ.scale 2 a
               case compare discr 0 of
                  LT   -> return false
                  EQ   -> return $ orList [-b/twoA]
                  GT   -> return $ orList [(-b+sdiscr)/twoA, (-b-sdiscr)/twoA]
            [a, b]     -> return $ orList [-b/a]
            [a] | a==0 -> return true
            _          -> return false
   
   g ors = 
      case disjunctions ors of
         Nothing -> 0 :==: 0
         Just xs -> 
            let make (x, a) = Var x .-. build (squareRootViewWith rationalView) a
            in build productView (False, map make xs) :==: 0

higherDegreeEquationsView :: View (OrList (Equation Expr)) (OrList Expr)
higherDegreeEquationsView = makeView f (fmap g)
 where
   f = let make (a :==: b) = orList (filter (not . hasNegSqrt) $ map cleanUpExpr2 (normHDE (a-b)))
       in Just . normalize . join . fmap make . cuRules

   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

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 = 
   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
      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
      
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