{-# LANGUAGE BlockArguments, LambdaCase, OverloadedStrings #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}
module Data.Derivation.Constraint (
Constraint, equal, greatEqualThan, greatThan, vars, has,
isDerivFrom, positives, selfContained, eliminate,
Poly, (.+), (.-) ) where
import Prelude hiding (null, filter)
import Control.Monad (guard)
import Data.Map.Strict (Map, null, singleton, (!?), filter, toList, lookupMin)
import Data.Map.Merge.Strict (
merge, preserveMissing, mapMissing,
zipWithMatched, zipWithMaybeMatched )
import Data.Maybe (isJust)
import Data.String (IsString, fromString)
import Data.Log (Log, logVar, (.+.), intersperse, Loggable(..))
---------------------------------------------------------------------------
-- * CONSTRAINT
-- + DATA CONSTRAINT
-- + CONSTRUCT
-- + READ
-- + CONVERT
-- * POLYNOMIAL
-- + TYPE POLY
-- + CONSTRUCT
-- + READ
-- + CONVERT
---------------------------------------------------------------------------
-- CONSTRAINT
---------------------------------------------------------------------------
-- DATA CONSTRAINT
data Constraint v = Eq (Poly v) | Geq (Poly v) deriving (Show, Eq, Ord)
constraint :: (Poly v -> a) -> (Poly v -> a) -> Constraint v -> a
constraint f g = \case Eq p -> f p; Geq p -> g p
instance IsString s => Loggable s v (Constraint v) where
log = constraint
(\p -> "(" .+. polyToLog (toList p) .+. " == 0)")
(\p -> "(" .+. polyToLog (toList p) .+. " >= 0)")
-- CONSTRUCT
equal :: Ord v => Poly v -> Poly v -> Constraint v
l `equal` r = Eq . posit . reduce $ l .- r
greatEqualThan :: Ord v => Poly v -> Poly v -> Constraint v
l `greatEqualThan` r = Geq . reduce $ l .- r
greatThan :: Ord v => Poly v -> Poly v -> Constraint v
l `greatThan` r = Geq $ reduce (l .- r) .- singleton Nothing 1
-- READ
vars :: Ord v => Constraint v -> [Maybe v]
vars = (fst <$>) . constraint toList toList
has :: Ord v => Constraint v -> Maybe v -> Bool
has = constraint (\p -> isJust . (p !?)) (\p -> isJust . (p !?))
selfContained :: Constraint v -> Bool
selfContained = constraint null $ all (>= 0)
isDerivFrom :: Ord v => Constraint v -> Constraint v -> Bool
Eq w `isDerivFrom` Eq g = w == g
Geq w `isDerivFrom` Eq g = w `isGeqThan` g
Geq w `isDerivFrom` Geq g = w `isGeqThan` g
_ `isDerivFrom` _ = False
-- CONVERT
positives :: Constraint v -> Constraint v
positives = constraint Eq $ Geq . filter (>= 0)
eliminate ::
Ord v => Maybe v -> Constraint v -> Constraint v -> Maybe (Constraint v)
eliminate v (Eq l) (Eq r) = Eq . posit . reduce . uncurry (.+) <$> alignEE v l r
eliminate v (Eq l) (Geq r) = Geq . reduce . uncurry (.+) <$> alignEG v l r
eliminate v (Geq l) (Geq r) = Geq . reduce . uncurry (.+) <$> alignGG v l r
eliminate v l r = eliminate v r l
type Aligned v = Maybe (Poly v, Poly v)
alignEE :: Ord v => Maybe v -> Poly v -> Poly v -> Aligned v
alignEE v l r =
(<$> ((,) <$> l !? v <*> r !? v)) \(m, s) -> (l `mul` s, r `mul` (- m))
alignEG :: Ord v => Maybe v -> Poly v -> Poly v -> Aligned v
alignEG v l r = (<$> ((,) <$> l !? v <*> r !? v)) \(m, s) ->
(l `mul` (- signum m * s), r `mul` abs m)
alignGG :: Ord v => Maybe v -> Poly v -> Poly v -> Aligned v
alignGG v l r = (,) <$> l !? v <*> r !? v >>= \(m, s) ->
(l `mul` abs s, r `mul` abs m) <$ guard (m * s < 0)
---------------------------------------------------------------------------
-- POLYNOMIAL
---------------------------------------------------------------------------
-- TYPE POLY
type Poly v = Map (Maybe v) Integer
polyToLog :: IsString s => [(Maybe v, Integer)] -> Log s v
polyToLog [] = "0"
polyToLog ps = intersperse " + " $ polyToLog1 <$> ps
polyToLog1 :: IsString s => (Maybe v, Integer) -> Log s v
polyToLog1 (Nothing, n) = fromString $ show n
polyToLog1 (Just v, n) = fromString (show n ++ " * ") .+. logVar v
-- CONSTRUCT
(.+), (.-) :: Ord v => Poly v -> Poly v -> Poly v
(.+) = merge preserveMissing preserveMissing
(zipWithMaybeMatched \_ a b -> (<$) <$> id <*> guard . (/= 0) $ a + b)
(.-) = merge preserveMissing (mapMissing $ const negate)
(zipWithMaybeMatched \_ a b -> (<$) <$> id <*> guard . (/= 0) $ a - b)
-- READ
isGeqThan :: Ord v => Poly v -> Poly v -> Bool
isGeqThan = (and .) . merge
(mapMissing \_ nl -> nl >= 0)
(mapMissing \_ nr -> nr <= 0) (zipWithMatched $ const (>=))
-- CONVERT
posit :: Poly v -> Poly v
posit p = p `maybe` ((p `mul`) . signum . snd) $ lookupMin p
reduce :: Poly v -> Poly v
reduce = divide <$> id <*> foldr gcd 0
mul, divide :: Poly v -> Integer -> Poly v
mul p = (<$> p) . (*); divide p = (<$> p) . flip div